mukul-chauhan-methdai Claude Opus 4.8 commited on
Commit
45aee1a
·
1 Parent(s): 54b6d55

Fix Control entry flow, hide persona editor, polish dashboard

Browse files

Operators opening the app in Reachy Mini Control landed on the persona/
API console (raw prompt + tools.txt editing) instead of the dashboard.

- index.html now redirects straight to /dashboard so the dashboard is
the greeting screen
- Move the persona/prompt/tools studio into personality.html (markup
unchanged so main.js keeps driving it), reachable from a new
"Advanced" button in the dashboard header
- Dashboard polish: de-duplicate State (top strip now shows Known
Guests instead of a second State pill), collapse the developer-only
"Last Face Event -> Model" block, wrap the header for Control's narrow
panel, warmer empty states
- Fix header face confidence rendering: it multiplied an LBPH distance
by 100 and showed nonsense like "5037%"; now shows the raw LBPH value,
consistent with the rest of the UI

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

src/reachy_mini_receptionist/static/dashboard.html CHANGED
@@ -289,6 +289,7 @@
289
 
290
  header {
291
  display: flex; align-items: center; gap: 12px;
 
292
  padding: 14px 24px; border-bottom: 1px solid var(--border);
293
  background: var(--panel);
294
  }
@@ -398,6 +399,16 @@
398
  letter-spacing: 0.08em; color: var(--muted);
399
  padding: 0 8px 6px; text-align: center;
400
  }
 
 
 
 
 
 
 
 
 
 
401
  .event-compact-list { list-style: none; display: flex; flex-direction: column; gap: 4px; padding: 0 8px 8px; }
402
  .event-compact-item { padding: 4px 0; border-top: 1px solid rgba(255,255,255,0.05); }
403
  .event-compact-key {
@@ -948,8 +959,17 @@
948
  onerror="this.style.display='none'; this.nextElementSibling.style.display='inline';" />
949
  <span class="brand-logo-fallback" style="font-size:1.4rem; display:none;">🤖</span>
950
  <h1>MethdAI Receptionist</h1>
 
 
 
 
 
 
 
 
 
951
  <button class="btn icon-only" id="btn-theme-toggle"
952
- style="margin-left:auto"
953
  title="Toggle light / dark theme">
954
  <span id="theme-toggle-icon">🌙</span>
955
  </button>
@@ -973,7 +993,7 @@
973
  <button id="volume-mute" style="background:none; border:none; cursor:pointer;
974
  color:var(--text); font-size:0.95rem; padding:0; line-height:1;">🔊</button>
975
  <input type="range" id="volume-slider" min="0" max="100" value="80"
976
- style="width:90px; accent-color:var(--accent);" />
977
  <span id="volume-label" style="min-width:32px; text-align:right;">—</span>
978
  </div>
979
 
@@ -1020,12 +1040,14 @@
1020
  <span class="stat-value dim" id="stat-last-visit">—</span>
1021
  <span class="stat-sub" id="stat-last-visit-time">—</span>
1022
  </div>
 
 
 
 
1023
  <div class="stat-card">
1024
- <span class="stat-label">Current State</span>
1025
- <span class="stat-value dim" id="stat-current-state-wrap">
1026
- <span class="session-state-pill idle" id="stat-current-state">idle</span>
1027
- </span>
1028
- <span class="stat-sub" id="stat-current-visitor">No active visitor</span>
1029
  </div>
1030
  </div>
1031
 
@@ -1052,8 +1074,8 @@
1052
  <div class="best-face-name unknown" id="best-face-name">—</div>
1053
  <div class="best-face-meta" id="best-face-meta">No detection</div>
1054
 
1055
- <div class="best-face-events-panel">
1056
- <div class="event-title">🧠 Last Face Event Sent To Model</div>
1057
  <ul class="event-compact-list">
1058
  <li class="event-compact-item">
1059
  <div class="event-compact-key">Status</div>
@@ -1068,7 +1090,7 @@
1068
  <div class="event-compact-val" id="event-previous">—</div>
1069
  </li>
1070
  </ul>
1071
- </div>
1072
  </div>
1073
  </div>
1074
  </div>
@@ -1142,7 +1164,7 @@
1142
  </div>
1143
  <div class="panel-body">
1144
  <ul class="guest-list" id="guest-list">
1145
- <li class="empty">No guests registered yet</li>
1146
  </ul>
1147
  </div>
1148
  </div>
@@ -1155,7 +1177,7 @@
1155
  </div>
1156
  <div class="panel-body">
1157
  <ul class="outbox-list" id="outbox-list">
1158
- <li class="empty">No emails sent yet</li>
1159
  </ul>
1160
  </div>
1161
  </div>
@@ -1350,7 +1372,11 @@
1350
  if (d.is_known) {
1351
  dot.className = 'dot known';
1352
  label.textContent = d.name;
1353
- conf.textContent = `${Math.round(d.confidence * 100)}%`;
 
 
 
 
1354
  overlay.textContent = `✓ Known: ${d.name}`;
1355
  overlay.className = 'video-overlay known';
1356
  } else {
@@ -1409,7 +1435,7 @@
1409
  const r = await fetch(`${BASE}/api/calendar`);
1410
  const appts = await r.json();
1411
  const ul = document.getElementById('calendar-list');
1412
- if (!appts.length) { ul.innerHTML = '<li class="empty">No appointments today</li>'; return; }
1413
  ul.innerHTML = appts.map(a => `
1414
  <li class="appt-item">
1415
  <span class="appt-time">${a.time}</span>
@@ -1429,7 +1455,9 @@
1429
  const guests = await r.json();
1430
  const ul = document.getElementById('guest-list');
1431
  document.getElementById('guest-count').textContent = guests.length ? `${guests.length}` : '';
1432
- if (!guests.length) { ul.innerHTML = '<li class="empty">No guests registered yet</li>'; return; }
 
 
1433
  ul.innerHTML = guests.map(g => `
1434
  <li class="guest-item">
1435
  <div class="guest-avatar">
@@ -1464,7 +1492,7 @@
1464
  const ul = document.getElementById('outbox-list');
1465
  document.getElementById('outbox-count').textContent = items.length ? `${items.length}` : '';
1466
  if (!items.length) {
1467
- ul.innerHTML = '<li class="empty">No emails sent yet</li>';
1468
  _lastOutboxCount = 0;
1469
  return;
1470
  }
@@ -1545,13 +1573,8 @@
1545
  lvTimeEl.textContent = 'No visits yet';
1546
  }
1547
 
1548
- const stateEl = document.getElementById('stat-current-state');
1549
- const state = d.current_state || 'idle';
1550
- stateEl.textContent = state.replaceAll('_', ' ');
1551
- stateEl.className = `session-state-pill ${state}`;
1552
-
1553
- const visEl = document.getElementById('stat-current-visitor');
1554
- visEl.textContent = d.current_visitor ? `Visitor: ${d.current_visitor}` : 'No active visitor';
1555
  } catch(e) {}
1556
  }
1557
 
 
289
 
290
  header {
291
  display: flex; align-items: center; gap: 12px;
292
+ flex-wrap: wrap; row-gap: 10px; /* wrap cleanly in Control's narrow panel */
293
  padding: 14px 24px; border-bottom: 1px solid var(--border);
294
  background: var(--panel);
295
  }
 
399
  letter-spacing: 0.08em; color: var(--muted);
400
  padding: 0 8px 6px; text-align: center;
401
  }
402
+ /* Collapsible wrapper — developer detail, closed by default so the
403
+ camera panel reads clean during a demo. */
404
+ details.best-face-events-panel > summary {
405
+ cursor: pointer; list-style: none; user-select: none;
406
+ }
407
+ details.best-face-events-panel > summary::-webkit-details-marker { display: none; }
408
+ details.best-face-events-panel > summary::after {
409
+ content: ' ▸'; color: var(--muted); font-size: 0.7rem;
410
+ }
411
+ details.best-face-events-panel[open] > summary::after { content: ' ▾'; }
412
  .event-compact-list { list-style: none; display: flex; flex-direction: column; gap: 4px; padding: 0 8px 8px; }
413
  .event-compact-item { padding: 4px 0; border-top: 1px solid rgba(255,255,255,0.05); }
414
  .event-compact-key {
 
959
  onerror="this.style.display='none'; this.nextElementSibling.style.display='inline';" />
960
  <span class="brand-logo-fallback" style="font-size:1.4rem; display:none;">🤖</span>
961
  <h1>MethdAI Receptionist</h1>
962
+ <!-- Advanced / Developer: opens the persona + prompt + tools studio.
963
+ Kept off the default flow (operators land on this dashboard, not the
964
+ studio); this is the deliberate escape hatch for editing the robot's
965
+ personality when you actually need to. -->
966
+ <a class="btn" id="btn-advanced" href="/static/personality.html"
967
+ style="margin-left:auto; text-decoration:none;"
968
+ title="Developer: edit the robot's persona, prompt & enabled tools">
969
+ 🧑‍💻 Advanced
970
+ </a>
971
  <button class="btn icon-only" id="btn-theme-toggle"
972
+ style="margin-left:8px"
973
  title="Toggle light / dark theme">
974
  <span id="theme-toggle-icon">🌙</span>
975
  </button>
 
993
  <button id="volume-mute" style="background:none; border:none; cursor:pointer;
994
  color:var(--text); font-size:0.95rem; padding:0; line-height:1;">🔊</button>
995
  <input type="range" id="volume-slider" min="0" max="100" value="80"
996
+ style="width:72px; accent-color:var(--accent);" />
997
  <span id="volume-label" style="min-width:32px; text-align:right;">—</span>
998
  </div>
999
 
 
1040
  <span class="stat-value dim" id="stat-last-visit">—</span>
1041
  <span class="stat-sub" id="stat-last-visit-time">—</span>
1042
  </div>
1043
+ <!-- Known Guests KPI. Replaced the old "Current State" card here — that
1044
+ value was a duplicate of the State pill in the Active Session panel
1045
+ below. The authoritative live state (with the journey trail + LLM
1046
+ cue) lives there; this slot now surfaces a non-duplicative metric. -->
1047
  <div class="stat-card">
1048
+ <span class="stat-label">Known Guests</span>
1049
+ <span class="stat-value" id="stat-known-guests">—</span>
1050
+ <span class="stat-sub">Registered faces</span>
 
 
1051
  </div>
1052
  </div>
1053
 
 
1074
  <div class="best-face-name unknown" id="best-face-name">—</div>
1075
  <div class="best-face-meta" id="best-face-meta">No detection</div>
1076
 
1077
+ <details class="best-face-events-panel">
1078
+ <summary class="event-title">🧠 Last Face Event Model</summary>
1079
  <ul class="event-compact-list">
1080
  <li class="event-compact-item">
1081
  <div class="event-compact-key">Status</div>
 
1090
  <div class="event-compact-val" id="event-previous">—</div>
1091
  </li>
1092
  </ul>
1093
+ </details>
1094
  </div>
1095
  </div>
1096
  </div>
 
1164
  </div>
1165
  <div class="panel-body">
1166
  <ul class="guest-list" id="guest-list">
1167
+ <li class="empty">No visitors registered yet — new faces appear here after they check in.</li>
1168
  </ul>
1169
  </div>
1170
  </div>
 
1177
  </div>
1178
  <div class="panel-body">
1179
  <ul class="outbox-list" id="outbox-list">
1180
+ <li class="empty">No host notifications yet — emails to hosts will appear here.</li>
1181
  </ul>
1182
  </div>
1183
  </div>
 
1372
  if (d.is_known) {
1373
  dot.className = 'dot known';
1374
  label.textContent = d.name;
1375
+ // `confidence` is an LBPH *distance* (lower = better match), not a
1376
+ // 0–1 probability — multiplying by 100 produced nonsense like
1377
+ // "5037%" in the header. Show the raw LBPH value, consistent with
1378
+ // the Best-Face panel and the session cue below.
1379
+ conf.textContent = `LBPH ${(Number(d.confidence) || 0).toFixed(1)}`;
1380
  overlay.textContent = `✓ Known: ${d.name}`;
1381
  overlay.className = 'video-overlay known';
1382
  } else {
 
1435
  const r = await fetch(`${BASE}/api/calendar`);
1436
  const appts = await r.json();
1437
  const ul = document.getElementById('calendar-list');
1438
+ if (!appts.length) { ul.innerHTML = "<li class=\"empty\">No appointments on today's calendar.</li>"; return; }
1439
  ul.innerHTML = appts.map(a => `
1440
  <li class="appt-item">
1441
  <span class="appt-time">${a.time}</span>
 
1455
  const guests = await r.json();
1456
  const ul = document.getElementById('guest-list');
1457
  document.getElementById('guest-count').textContent = guests.length ? `${guests.length}` : '';
1458
+ const kg = document.getElementById('stat-known-guests');
1459
+ if (kg) kg.textContent = `${guests.length}`;
1460
+ if (!guests.length) { ul.innerHTML = '<li class="empty">No visitors registered yet — new faces appear here after they check in.</li>'; return; }
1461
  ul.innerHTML = guests.map(g => `
1462
  <li class="guest-item">
1463
  <div class="guest-avatar">
 
1492
  const ul = document.getElementById('outbox-list');
1493
  document.getElementById('outbox-count').textContent = items.length ? `${items.length}` : '';
1494
  if (!items.length) {
1495
+ ul.innerHTML = '<li class="empty">No host notifications yet — emails to hosts will appear here.</li>';
1496
  _lastOutboxCount = 0;
1497
  return;
1498
  }
 
1573
  lvTimeEl.textContent = 'No visits yet';
1574
  }
1575
 
1576
+ // Current state / visitor intentionally NOT rendered here anymore —
1577
+ // they duplicated the Active Session panel. See the Known Guests card.
 
 
 
 
 
1578
  } catch(e) {}
1579
  }
1580
 
src/reachy_mini_receptionist/static/index.html CHANGED
@@ -3,118 +3,28 @@
3
  <head>
4
  <meta charset="utf-8" />
5
  <meta name="viewport" content="width=device-width, initial-scale=1" />
6
- <title>Reachy Mini Conversation – Settings</title>
7
- <link rel="stylesheet" href="/static/style.css" />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  </head>
9
  <body>
10
- <div class="ambient"></div>
11
- <div id="loading" class="loading">
12
- <div class="spinner"></div>
13
- <p>Loading…</p>
14
- </div>
15
- <div class="container">
16
- <div class="dashboard-banner" role="navigation" aria-label="Dashboard quick link">
17
- <a href="/dashboard">Open Receptionist Dashboard</a>
18
- </div>
19
-
20
- <header class="hero">
21
- <div class="pill">Headless control</div>
22
- <h1>Reachy Mini Conversation</h1>
23
- <p class="subtitle">Configure your OpenAI key and tweak personalities without the full UI.</p>
24
- </header>
25
-
26
- <div id="configured" class="panel hidden">
27
- <div class="panel-heading">
28
- <div>
29
- <p class="eyebrow">Credentials</p>
30
- <h2>API key ready</h2>
31
- </div>
32
- <span class="chip chip-ok">Connected</span>
33
- </div>
34
- <p class="muted">OpenAI API key is already configured. You can jump straight to personalities.</p>
35
- <button id="change-key-btn" class="ghost">Change API key</button>
36
- </div>
37
-
38
- <div id="form-panel" class="panel hidden">
39
- <div class="panel-heading">
40
- <div>
41
- <p class="eyebrow">Credentials</p>
42
- <h2>Connect OpenAI</h2>
43
- </div>
44
- <span class="chip">Required</span>
45
- </div>
46
- <p class="muted">Paste your API key once and we will store it locally for the headless conversation loop.</p>
47
- <label for="api-key">OpenAI API Key</label>
48
- <input id="api-key" type="password" placeholder="sk-..." autocomplete="off" />
49
- <div class="actions">
50
- <button id="save-btn">Save key</button>
51
- <p id="status" class="status"></p>
52
- </div>
53
- </div>
54
-
55
- <div id="personality-panel" class="panel hidden">
56
- <div class="panel-heading">
57
- <div>
58
- <p class="eyebrow">Profiles</p>
59
- <h2>Personality studio</h2>
60
- </div>
61
- <span class="chip">Live</span>
62
- </div>
63
- <p class="muted">Create lean instruction sets, toggle tools, and apply a voice for your Reachy Mini.</p>
64
- <div class="section">
65
- <div class="section-heading">
66
- <h3>Select & launch</h3>
67
- <p class="muted small">Pick a profile and choose what should launch on startup.</p>
68
- </div>
69
- <div class="row row-top">
70
- <label for="personality-select">Select</label>
71
- <select id="personality-select"></select>
72
- <button id="persist-personality" class="ghost">Use on startup</button>
73
- <button id="apply-personality" class="ghost">Apply</button>
74
- <button id="new-personality" class="ghost">New</button>
75
- </div>
76
- <div class="row">
77
- <label>Startup personality</label>
78
- <div class="startup-row">
79
- <span id="startup-label" class="chip">Built-in default</span>
80
- </div>
81
- </div>
82
- </div>
83
-
84
- <div class="section">
85
- <div class="section-heading">
86
- <h3>Create / edit</h3>
87
- <p class="muted small">Adjust instructions, tools, and voice, then save your profile.</p>
88
- </div>
89
- <div class="row">
90
- <label for="personality-name">Name</label>
91
- <input id="personality-name" type="text" class="input-field" placeholder="my_profile" />
92
- <label for="voice-select">Voice</label>
93
- <select id="voice-select"></select>
94
- </div>
95
- <div class="row">
96
- <label for="instructions-ta">Instructions</label>
97
- <textarea id="instructions-ta" rows="8" placeholder="# Write your instructions here"></textarea>
98
- </div>
99
- <div class="row">
100
- <label for="tools-ta">tools.txt</label>
101
- <textarea id="tools-ta" rows="6" placeholder="# tools enabled for this profile"></textarea>
102
- </div>
103
- <div class="row">
104
- <label for="tools-available">Available tools</label>
105
- <div id="tools-available" class="checkbox-grid"></div>
106
- </div>
107
- <div class="row row-save">
108
- <label></label>
109
- <div class="actions">
110
- <button id="save-personality">Save</button>
111
- </div>
112
- </div>
113
- </div>
114
- <p id="personality-status" class="status"></p>
115
- </div>
116
- </div>
117
-
118
- <script src="/static/main.js"></script>
119
  </body>
120
- </html>
 
3
  <head>
4
  <meta charset="utf-8" />
5
  <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>MethdAI Receptionist</title>
7
+ <!-- The app landing page now goes straight to the operator dashboard.
8
+ The old persona/API console lived here and was the first thing an
9
+ operator saw in Reachy Mini Control — it exposed raw prompt/tools
10
+ editing that end users should never touch. That studio moved to
11
+ /static/personality.html and is reachable from the dashboard's
12
+ "Advanced" button. Redirect via meta-refresh + JS (covers Control's
13
+ embedded webview) with a manual link as a no-JS fallback. -->
14
+ <meta http-equiv="refresh" content="0; url=/dashboard" />
15
+ <script>
16
+ window.location.replace("/dashboard");
17
+ </script>
18
+ <style>
19
+ body {
20
+ margin: 0; min-height: 100vh; display: flex; align-items: center;
21
+ justify-content: center; background: #0e1118; color: #e2e8f0;
22
+ font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
23
+ }
24
+ a { color: #818cf8; }
25
+ </style>
26
  </head>
27
  <body>
28
+ <p>Opening the Receptionist Dashboard… <a href="/dashboard">Continue</a></p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  </body>
30
+ </html>
src/reachy_mini_receptionist/static/personality.html ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>Advanced — Personality Studio</title>
7
+ <link rel="stylesheet" href="/static/style.css" />
8
+ </head>
9
+ <body>
10
+ <div class="ambient"></div>
11
+ <div id="loading" class="loading">
12
+ <div class="spinner"></div>
13
+ <p>Loading…</p>
14
+ </div>
15
+ <div class="container">
16
+ <div class="dashboard-banner" role="navigation" aria-label="Dashboard quick link">
17
+ <a href="/dashboard">← Back to Receptionist Dashboard</a>
18
+ </div>
19
+
20
+ <header class="hero">
21
+ <div class="pill">Advanced · Developer</div>
22
+ <h1>Personality studio</h1>
23
+ <p class="subtitle">Edit the robot's persona, prompt, and enabled tools. Not needed for normal operation.</p>
24
+ </header>
25
+
26
+ <div id="configured" class="panel hidden">
27
+ <div class="panel-heading">
28
+ <div>
29
+ <p class="eyebrow">Credentials</p>
30
+ <h2>API key ready</h2>
31
+ </div>
32
+ <span class="chip chip-ok">Connected</span>
33
+ </div>
34
+ <p class="muted">OpenAI API key is already configured. You can jump straight to personalities.</p>
35
+ <button id="change-key-btn" class="ghost">Change API key</button>
36
+ </div>
37
+
38
+ <div id="form-panel" class="panel hidden">
39
+ <div class="panel-heading">
40
+ <div>
41
+ <p class="eyebrow">Credentials</p>
42
+ <h2>Connect OpenAI</h2>
43
+ </div>
44
+ <span class="chip">Required</span>
45
+ </div>
46
+ <p class="muted">Paste your API key once and we will store it locally for the headless conversation loop.</p>
47
+ <label for="api-key">OpenAI API Key</label>
48
+ <input id="api-key" type="password" placeholder="sk-..." autocomplete="off" />
49
+ <div class="actions">
50
+ <button id="save-btn">Save key</button>
51
+ <p id="status" class="status"></p>
52
+ </div>
53
+ </div>
54
+
55
+ <div id="personality-panel" class="panel hidden">
56
+ <div class="panel-heading">
57
+ <div>
58
+ <p class="eyebrow">Profiles</p>
59
+ <h2>Personality studio</h2>
60
+ </div>
61
+ <span class="chip">Live</span>
62
+ </div>
63
+ <p class="muted">Create lean instruction sets, toggle tools, and apply a voice for your Reachy Mini.</p>
64
+ <div class="section">
65
+ <div class="section-heading">
66
+ <h3>Select & launch</h3>
67
+ <p class="muted small">Pick a profile and choose what should launch on startup.</p>
68
+ </div>
69
+ <div class="row row-top">
70
+ <label for="personality-select">Select</label>
71
+ <select id="personality-select"></select>
72
+ <button id="persist-personality" class="ghost">Use on startup</button>
73
+ <button id="apply-personality" class="ghost">Apply</button>
74
+ <button id="new-personality" class="ghost">New</button>
75
+ </div>
76
+ <div class="row">
77
+ <label>Startup personality</label>
78
+ <div class="startup-row">
79
+ <span id="startup-label" class="chip">Built-in default</span>
80
+ </div>
81
+ </div>
82
+ </div>
83
+
84
+ <div class="section">
85
+ <div class="section-heading">
86
+ <h3>Create / edit</h3>
87
+ <p class="muted small">Adjust instructions, tools, and voice, then save your profile.</p>
88
+ </div>
89
+ <div class="row">
90
+ <label for="personality-name">Name</label>
91
+ <input id="personality-name" type="text" class="input-field" placeholder="my_profile" />
92
+ <label for="voice-select">Voice</label>
93
+ <select id="voice-select"></select>
94
+ </div>
95
+ <div class="row">
96
+ <label for="instructions-ta">Instructions</label>
97
+ <textarea id="instructions-ta" rows="8" placeholder="# Write your instructions here"></textarea>
98
+ </div>
99
+ <div class="row">
100
+ <label for="tools-ta">tools.txt</label>
101
+ <textarea id="tools-ta" rows="6" placeholder="# tools enabled for this profile"></textarea>
102
+ </div>
103
+ <div class="row">
104
+ <label for="tools-available">Available tools</label>
105
+ <div id="tools-available" class="checkbox-grid"></div>
106
+ </div>
107
+ <div class="row row-save">
108
+ <label></label>
109
+ <div class="actions">
110
+ <button id="save-personality">Save</button>
111
+ </div>
112
+ </div>
113
+ </div>
114
+ <p id="personality-status" class="status"></p>
115
+ </div>
116
+ </div>
117
+
118
+ <script src="/static/main.js"></script>
119
+ </body>
120
+ </html>