mrsavage1 commited on
Commit
910505d
·
verified ·
1 Parent(s): d06b6f9

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +119 -46
index.html CHANGED
@@ -3,60 +3,93 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
6
- <title>IONA | Server Configuration</title>
7
  <script src="https://unpkg.com/@phosphor-icons/web"></script>
8
  <style>
9
- :root { --bg: #0b0f19; --card: #161b22; --accent: #7c3aed; --text: #f0f6fc; --muted: #8b949e; --border: #30363d; --success: #3fb950; }
10
- * { margin: 0; padding: 0; box-sizing: border-box; font-family: sans-serif; }
11
- body { background: var(--bg); color: var(--text); padding: 15px; display: flex; justify-content: center; }
12
  .wrapper { width: 100%; max-width: 800px; }
 
 
13
  .header { display: flex; justify-content: space-between; align-items: center; padding: 10px 0 25px; }
14
- .status-pill { font-size: 0.7rem; color: var(--success); background: rgba(63, 185, 80, 0.1); padding: 6px 12px; border-radius: 20px; border: 1px solid rgba(63, 185, 80, 0.2); }
 
 
 
15
  .stats-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin-bottom: 20px; }
16
- .stat-box { background: var(--card); padding: 15px; border-radius: 12px; text-align: center; border: 1px solid var(--border); }
17
- .label { display: block; font-size: 0.6rem; color: var(--muted); margin-bottom: 6px; }
 
18
  .val { font-size: 0.9rem; font-weight: 700; color: #fff; }
19
- .card { background: var(--card); padding: 18px; border-radius: 12px; border: 1px solid var(--border); margin-bottom: 15px; }
20
- .card h3 { font-size: 0.85rem; color: var(--accent); margin-bottom: 15px; }
21
- .tags { display: flex; flex-wrap: wrap; gap: 8px; }
22
- .tags span { background: #21262d; padding: 5px 10px; border-radius: 6px; font-size: 0.75rem; border: 1px solid var(--border); color: #c9d1d9; }
23
- .log-window { background: #0d1117; height: 150px; padding: 12px; border-radius: 8px; font-family: monospace; font-size: 0.7rem; overflow-y: auto; border: 1px solid #21262d; }
24
- .log-line { margin-bottom: 6px; color: #8b949e; }
25
- .log-line span { color: var(--accent); }
26
- .btn { width: 100%; padding: 16px; border-radius: 10px; border: none; font-weight: 700; font-size: 0.8rem; cursor: pointer; background: var(--accent); color: white; margin-top: 15px; }
27
- input, select { background: #0d1117; color: white; border: 1px solid var(--border); padding: 12px; border-radius: 8px; width: 100%; margin-top: 10px; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  </style>
29
  </head>
30
  <body>
31
  <div class="wrapper">
32
  <header class="header">
33
  <div class="brand"><h1>IONA <span style="font-weight: 400; color: var(--muted); font-size: 0.7rem;">NEXUS SERVER</span></h1></div>
34
- <div class="status-pill"> System Online</div>
35
  </header>
36
 
37
  <div class="stats-row">
38
- <div class="stat-box"><span class="label">UPTIME</span><span class="val" id="uptime">--</span></div>
39
- <div class="stat-box"><span class="label">RAM</span><span class="val" id="ram">--</span></div>
40
- <div class="stat-box"><span class="label">CPU</span><span class="val" id="cpu">--</span></div>
41
  </div>
42
 
43
  <section class="card">
44
- <h3><i class="ph ph-waveform"></i> Voices Available</h3>
45
- <div class="tags" id="voice-tags"><span>Syncing...</span></div>
 
 
 
 
46
  </section>
47
 
48
  <section class="card">
49
- <h3><i class="ph ph-terminal"></i> Test Control</h3>
50
- <input type="text" id="test-text" value="Iona Nexus is fully operational. System green.">
51
- <div style="display: flex; gap: 10px;">
52
  <select id="voice-cat" onchange="updateSubDropdown()"></select>
53
  <select id="voice-sub"></select>
54
  </div>
55
- <button class="btn" onclick="synthesize()">EXECUTE SYNTHESIS</button>
 
 
56
  </section>
57
 
58
  <section class="card">
59
- <h3><i class="ph ph-terminal-window"></i> Live System Logs</h3>
60
  <div class="log-window" id="console"></div>
61
  </section>
62
  </div>
@@ -72,40 +105,80 @@
72
  document.getElementById('uptime').innerText = data.uptime;
73
  document.getElementById('ram').innerText = data.ram;
74
  document.getElementById('cpu').innerText = data.cpu;
75
- document.getElementById('console').innerHTML = data.logs.map(l => `<div class="log-line"><span>></span> ${l}</div>`).join('');
76
 
77
- // Update dropdowns only if registry has changed
 
 
 
 
 
 
78
  if (JSON.stringify(currentRegistry) !== JSON.stringify(data.registry)) {
79
  currentRegistry = data.registry;
80
- const catSelect = document.getElementById('voice-cat');
81
- catSelect.innerHTML = Object.keys(currentRegistry).map(c => `<option value="${c}">${c}</option>`).join('');
82
-
83
- const tags = document.getElementById('voice-tags');
84
- tags.innerHTML = Object.keys(currentRegistry).map(c => `<span>${c}</span>`).join('');
85
-
86
- updateSubDropdown();
87
  }
88
- } catch (e) { console.error("Sync Error", e); }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  }
90
 
91
  function updateSubDropdown() {
92
  const cat = document.getElementById('voice-cat').value;
93
  const subSelect = document.getElementById('voice-sub');
 
 
 
 
 
 
94
  if (currentRegistry[cat]) {
95
  subSelect.innerHTML = currentRegistry[cat].map(s => `<option value="${s}">${s}</option>`).join('');
96
  }
97
  }
98
 
99
  async function synthesize() {
100
- const formData = new FormData();
101
- formData.append('text', document.getElementById('test-text').value);
102
- formData.append('category', document.getElementById('voice-cat').value);
103
- formData.append('voice', document.getElementById('voice-sub').value);
104
-
105
- const response = await fetch('/stream', { method: 'POST', body: formData });
106
- const blob = await response.blob();
107
- const audio = new Audio(URL.createObjectURL(blob));
108
- audio.play();
 
 
 
 
 
 
 
 
 
 
 
109
  }
110
 
111
  setInterval(refresh, 3000);
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
6
+ <title>IONA | Nexus Control</title>
7
  <script src="https://unpkg.com/@phosphor-icons/web"></script>
8
  <style>
9
+ :root { --bg: #0b0f19; --card: #161b22; --accent: #7c3aed; --text: #f0f6fc; --muted: #8b949e; --border: #30363d; --success: #3fb950; --error: #f85149; }
10
+ * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Inter', -apple-system, sans-serif; }
11
+ body { background: var(--bg); color: var(--text); padding: 15px; display: flex; justify-content: center; min-height: 100vh; }
12
  .wrapper { width: 100%; max-width: 800px; }
13
+
14
+ /* Header */
15
  .header { display: flex; justify-content: space-between; align-items: center; padding: 10px 0 25px; }
16
+ .brand h1 { font-size: 1.2rem; letter-spacing: 1px; }
17
+ .status-pill { font-size: 0.7rem; color: var(--success); background: rgba(63, 185, 80, 0.1); padding: 6px 12px; border-radius: 20px; border: 1px solid rgba(63, 185, 80, 0.2); display: flex; align-items: center; gap: 6px; }
18
+
19
+ /* Stats Dashboard */
20
  .stats-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin-bottom: 20px; }
21
+ .stat-box { background: var(--card); padding: 15px; border-radius: 12px; text-align: center; border: 1px solid var(--border); transition: transform 0.2s; }
22
+ .stat-box:hover { transform: translateY(-2px); border-color: var(--accent); }
23
+ .label { display: block; font-size: 0.6rem; color: var(--muted); margin-bottom: 6px; text-transform: uppercase; font-weight: 600; }
24
  .val { font-size: 0.9rem; font-weight: 700; color: #fff; }
25
+
26
+ /* Card System */
27
+ .card { background: var(--card); padding: 20px; border-radius: 14px; border: 1px solid var(--border); margin-bottom: 15px; }
28
+ .card-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; }
29
+ .card h3 { font-size: 0.85rem; color: var(--accent); display: flex; align-items: center; gap: 8px; }
30
+
31
+ /* Search & Tags */
32
+ .search-bar { background: #0d1117; border: 1px solid var(--border); padding: 8px 12px; border-radius: 8px; color: white; font-size: 0.8rem; width: 100%; margin-bottom: 12px; outline: none; }
33
+ .search-bar:focus { border-color: var(--accent); }
34
+ .tags { display: flex; flex-wrap: wrap; gap: 8px; max-height: 120px; overflow-y: auto; padding-right: 5px; }
35
+ .tags::-webkit-scrollbar { width: 4px; }
36
+ .tags::-webkit-scrollbar-thumb { background: var(--border); border-radius: 10px; }
37
+ .tags span { background: #21262d; padding: 5px 12px; border-radius: 6px; font-size: 0.75rem; border: 1px solid var(--border); color: #c9d1d9; cursor: default; transition: 0.2s; }
38
+ .tags span.active { background: var(--accent); color: white; border-color: var(--accent); }
39
+
40
+ /* Inputs */
41
+ .input-group { display: flex; gap: 10px; margin-top: 10px; }
42
+ input, select { background: #0d1117; color: white; border: 1px solid var(--border); padding: 12px; border-radius: 8px; width: 100%; outline: none; transition: 0.2s; }
43
+ input:focus, select:focus { border-color: var(--accent); }
44
+
45
+ /* Execution Button */
46
+ .btn { width: 100%; padding: 16px; border-radius: 10px; border: none; font-weight: 700; font-size: 0.85rem; cursor: pointer; background: var(--accent); color: white; margin-top: 15px; display: flex; align-items: center; justify-content: center; gap: 10px; transition: filter 0.2s; }
47
+ .btn:active { transform: scale(0.98); }
48
+ .btn:disabled { filter: grayscale(1); cursor: not-allowed; }
49
+
50
+ /* Logs */
51
+ .log-window { background: #0d1117; height: 180px; padding: 15px; border-radius: 10px; font-family: 'JetBrains Mono', monospace; font-size: 0.7rem; overflow-y: auto; border: 1px solid #21262d; line-height: 1.5; }
52
+ .log-line { margin-bottom: 4px; border-left: 2px solid transparent; padding-left: 8px; }
53
+ .log-line.synth { border-left-color: var(--accent); color: #e6edf3; }
54
+ .log-line span { color: var(--accent); font-weight: bold; margin-right: 5px; }
55
  </style>
56
  </head>
57
  <body>
58
  <div class="wrapper">
59
  <header class="header">
60
  <div class="brand"><h1>IONA <span style="font-weight: 400; color: var(--muted); font-size: 0.7rem;">NEXUS SERVER</span></h1></div>
61
+ <div id="status" class="status-pill"><i class="ph-fill ph-circle"></i> System Online</div>
62
  </header>
63
 
64
  <div class="stats-row">
65
+ <div class="stat-box"><span class="label">Uptime</span><span class="val" id="uptime">--</span></div>
66
+ <div class="stat-box"><span class="label">Memory</span><span class="val" id="ram">--</span></div>
67
+ <div class="stat-box"><span class="label">CPU Load</span><span class="val" id="cpu">--</span></div>
68
  </div>
69
 
70
  <section class="card">
71
+ <div class="card-header">
72
+ <h3><i class="ph ph-waveform"></i> Voice Library</h3>
73
+ <span id="voice-count" style="font-size: 0.7rem; color: var(--muted)">0 Loaded</span>
74
+ </div>
75
+ <input type="text" class="search-bar" id="voice-search" placeholder="Search voices (Alba, Cori, Amy...)" oninput="filterTags()">
76
+ <div class="tags" id="voice-tags"></div>
77
  </section>
78
 
79
  <section class="card">
80
+ <h3><i class="ph ph-terminal"></i> Controller</h3>
81
+ <input type="text" id="test-text" placeholder="Enter text to synthesize..." value="Iona Nexus is fully operational. Engine ready.">
82
+ <div class="input-group">
83
  <select id="voice-cat" onchange="updateSubDropdown()"></select>
84
  <select id="voice-sub"></select>
85
  </div>
86
+ <button class="btn" id="synth-btn" onclick="synthesize()">
87
+ <i class="ph ph-play-circle"></i> EXECUTE SYNTHESIS
88
+ </button>
89
  </section>
90
 
91
  <section class="card">
92
+ <h3><i class="ph ph-terminal-window"></i> System Logs</h3>
93
  <div class="log-window" id="console"></div>
94
  </section>
95
  </div>
 
105
  document.getElementById('uptime').innerText = data.uptime;
106
  document.getElementById('ram').innerText = data.ram;
107
  document.getElementById('cpu').innerText = data.cpu;
 
108
 
109
+ // Better Log Rendering
110
+ const consoleEl = document.getElementById('console');
111
+ consoleEl.innerHTML = data.logs.map(l => {
112
+ const isSynth = l.includes("SYNTH");
113
+ return `<div class="log-line ${isSynth ? 'synth' : ''}"><span>></span>${l}</div>`;
114
+ }).join('');
115
+
116
  if (JSON.stringify(currentRegistry) !== JSON.stringify(data.registry)) {
117
  currentRegistry = data.registry;
118
+ renderVoiceUI();
 
 
 
 
 
 
119
  }
120
+ } catch (e) {
121
+ document.getElementById('status').style.color = 'var(--error)';
122
+ document.getElementById('status').innerHTML = '<i class="ph-fill ph-circle"></i> Offline';
123
+ }
124
+ }
125
+
126
+ function renderVoiceUI() {
127
+ const categories = Object.keys(currentRegistry);
128
+ document.getElementById('voice-count').innerText = `${categories.length} Voices`;
129
+
130
+ const catSelect = document.getElementById('voice-cat');
131
+ catSelect.innerHTML = categories.map(c => `<option value="${c}">${c}</option>`).join('');
132
+
133
+ filterTags(); // Initial render of tags
134
+ updateSubDropdown();
135
+ }
136
+
137
+ function filterTags() {
138
+ const query = document.getElementById('voice-search').value.toLowerCase();
139
+ const tagsEl = document.getElementById('voice-tags');
140
+ const categories = Object.keys(currentRegistry);
141
+
142
+ tagsEl.innerHTML = categories
143
+ .filter(c => c.toLowerCase().includes(query))
144
+ .map(c => `<span>${c}</span>`).join('');
145
  }
146
 
147
  function updateSubDropdown() {
148
  const cat = document.getElementById('voice-cat').value;
149
  const subSelect = document.getElementById('voice-sub');
150
+
151
+ // Highlight active tag
152
+ document.querySelectorAll('#voice-tags span').forEach(s => {
153
+ s.classList.toggle('active', s.innerText === cat);
154
+ });
155
+
156
  if (currentRegistry[cat]) {
157
  subSelect.innerHTML = currentRegistry[cat].map(s => `<option value="${s}">${s}</option>`).join('');
158
  }
159
  }
160
 
161
  async function synthesize() {
162
+ const btn = document.getElementById('synth-btn');
163
+ btn.disabled = true;
164
+ btn.innerHTML = '<i class="ph ph-circle-notch ph-spin"></i> GENERATING...';
165
+
166
+ try {
167
+ const formData = new FormData();
168
+ formData.append('text', document.getElementById('test-text').value);
169
+ formData.append('category', document.getElementById('voice-cat').value);
170
+ formData.append('voice', document.getElementById('voice-sub').value);
171
+
172
+ const response = await fetch('/stream', { method: 'POST', body: formData });
173
+ const blob = await response.blob();
174
+ const audio = new Audio(URL.createObjectURL(blob));
175
+ await audio.play();
176
+ } catch (e) {
177
+ console.error(e);
178
+ } finally {
179
+ btn.disabled = false;
180
+ btn.innerHTML = '<i class="ph ph-play-circle"></i> EXECUTE SYNTHESIS';
181
+ }
182
  }
183
 
184
  setInterval(refresh, 3000);