UX: faster load, skeleton fix, About cleanup, name hint, Connect Apps docs

Loader:
- Post-init modules (engines, ai-backends, generation, conversation) now
  load AFTER the skeleton is removed instead of before. The UI is visible
  ~500 ms sooner on average; those four modules load while the user is
  already browsing Voices / Clone / Design.
- Removed the sttReady event approach that was triggering a duplicate
  /api/stt-backends call; init.js already populates all STT selects once
  on startup.

Skeleton:
- Replaced the card-grid placeholder with a two-column workbench skeleton
  (voice list rows on the left + inspector placeholder on the right) that
  matches the real My Voices layout.

Connect Apps:
- MCP Server, /speak REST endpoint, and Global hotkey daemon sections
  moved from Settings → About to Connect Apps, where they belong.
- About page now has GitHub + Releases links instead.

Clone section:
- Added a hint note beneath the sample-text textarea reminding the user
  to replace the placeholder name (Sam / Alex / Marco …) with their own.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
mARTin-B78 2026-05-31 19:19:20 +02:00
parent b3c1e4cc7f
commit 0b3109cbf4
7 changed files with 124 additions and 168 deletions

View File

@ -51,11 +51,15 @@
.sk-icon { width: 32px; height: 32px; border-radius: 8px; flex-shrink: 0; } .sk-icon { width: 32px; height: 32px; border-radius: 8px; flex-shrink: 0; }
.sk-title { display: flex; flex-direction: column; gap: 7px; } .sk-title { display: flex; flex-direction: column; gap: 7px; }
.sk-toolbar { display: flex; gap: 10px; align-items: center; } .sk-toolbar { display: flex; gap: 10px; align-items: center; }
.sk-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 12px; } .sk-row { display: flex; gap: 10px; align-items: center; }
.sk-card { border-radius: 10px; overflow: hidden; } .sk-workbench { display: grid; grid-template-columns: 300px 1fr; gap: 0; border: 1px solid var(--border,#e5e7eb); border-radius: 10px; overflow: hidden; min-height: 480px; }
.sk-card-img { height: 56px; border-radius: 0; } .sk-list { border-right: 1px solid var(--border,#e5e7eb); display: flex; flex-direction: column; }
.sk-card-body { padding: 10px 12px; display: flex; flex-direction: column; gap: 6px; } .sk-list-row { display: flex; align-items: center; gap: 10px; padding: 10px 12px; border-bottom: 1px solid var(--border,#e5e7eb); }
.sk-row { display: flex; gap: 10px; margin-top: 4px; } .sk-avatar { width: 36px; height: 36px; border-radius: 8px; flex-shrink: 0; }
.sk-list-body { display: flex; flex-direction: column; gap: 5px; flex: 1; min-width: 0; }
.sk-insp { padding: 20px; display: flex; flex-direction: column; gap: 16px; }
.sk-insp-head { display: flex; flex-direction: column; gap: 8px; }
@media (max-width: 700px) { .sk-workbench { grid-template-columns: 1fr; } .sk-insp { display: none; } }
</style> </style>
</head> </head>
<body> <body>
@ -74,6 +78,15 @@
<div class="tab" data-tab="settings" tabindex="-1"><span class="tab-title">Settings</span></div> <div class="tab" data-tab="settings" tabindex="-1"><span class="tab-title">Settings</span></div>
</nav> </nav>
<!-- Mobile top bar (hidden on desktop via CSS) -->
<div id="mobile-topbar">
<button id="sidebar-toggle" aria-label="Open navigation"><span class="mdi mdi-menu"></span></button>
<span class="mobile-brand"><span class="mdi mdi-microphone-variant"></span> Voice Creator</span>
</div>
<!-- Backdrop for sidebar drawer on mobile -->
<div id="sidebar-backdrop"></div>
<!-- App shell --> <!-- App shell -->
<div id="app-shell"> <div id="app-shell">
@ -168,98 +181,38 @@
<div class="sk" style="width:80px;height:30px;border-radius:6px"></div> <div class="sk" style="width:80px;height:30px;border-radius:6px"></div>
</div> </div>
<!-- Voice card grid (matches the voice library layout) --> <!-- Voices workbench skeleton (list + inspector, matches real My Voices layout) -->
<div class="sk-grid"> <div class="sk-workbench">
<!-- 8 placeholder voice cards --> <div class="sk-list">
<div class="sk-card" style="border:1px solid var(--border,#e5e7eb)"> <div class="sk-list-row"><div class="sk sk-avatar"></div><div class="sk-list-body"><div class="sk" style="width:75%;height:13px"></div><div class="sk" style="width:50%;height:11px"></div></div><div class="sk" style="width:28px;height:28px;border-radius:50%;flex-shrink:0"></div><div class="sk" style="width:28px;height:28px;border-radius:50%;flex-shrink:0"></div></div>
<div class="sk sk-card-img"></div> <div class="sk-list-row"><div class="sk sk-avatar"></div><div class="sk-list-body"><div class="sk" style="width:65%;height:13px"></div><div class="sk" style="width:40%;height:11px"></div></div><div class="sk" style="width:28px;height:28px;border-radius:50%;flex-shrink:0"></div><div class="sk" style="width:28px;height:28px;border-radius:50%;flex-shrink:0"></div></div>
<div class="sk-card-body"> <div class="sk-list-row"><div class="sk sk-avatar"></div><div class="sk-list-body"><div class="sk" style="width:80%;height:13px"></div><div class="sk" style="width:45%;height:11px"></div></div><div class="sk" style="width:28px;height:28px;border-radius:50%;flex-shrink:0"></div><div class="sk" style="width:28px;height:28px;border-radius:50%;flex-shrink:0"></div></div>
<div class="sk" style="width:70%;height:14px"></div> <div class="sk-list-row"><div class="sk sk-avatar"></div><div class="sk-list-body"><div class="sk" style="width:55%;height:13px"></div><div class="sk" style="width:35%;height:11px"></div></div><div class="sk" style="width:28px;height:28px;border-radius:50%;flex-shrink:0"></div><div class="sk" style="width:28px;height:28px;border-radius:50%;flex-shrink:0"></div></div>
<div class="sk" style="width:45%;height:12px"></div> <div class="sk-list-row"><div class="sk sk-avatar"></div><div class="sk-list-body"><div class="sk" style="width:70%;height:13px"></div><div class="sk" style="width:48%;height:11px"></div></div><div class="sk" style="width:28px;height:28px;border-radius:50%;flex-shrink:0"></div><div class="sk" style="width:28px;height:28px;border-radius:50%;flex-shrink:0"></div></div>
<div class="sk-row"> <div class="sk-list-row"><div class="sk sk-avatar"></div><div class="sk-list-body"><div class="sk" style="width:60%;height:13px"></div><div class="sk" style="width:42%;height:11px"></div></div><div class="sk" style="width:28px;height:28px;border-radius:50%;flex-shrink:0"></div><div class="sk" style="width:28px;height:28px;border-radius:50%;flex-shrink:0"></div></div>
<div class="sk" style="width:28px;height:28px;border-radius:50%"></div> <div class="sk-list-row"><div class="sk sk-avatar"></div><div class="sk-list-body"><div class="sk" style="width:85%;height:13px"></div><div class="sk" style="width:38%;height:11px"></div></div><div class="sk" style="width:28px;height:28px;border-radius:50%;flex-shrink:0"></div><div class="sk" style="width:28px;height:28px;border-radius:50%;flex-shrink:0"></div></div>
<div class="sk" style="width:28px;height:28px;border-radius:50%"></div> <div class="sk-list-row"><div class="sk sk-avatar"></div><div class="sk-list-body"><div class="sk" style="width:72%;height:13px"></div><div class="sk" style="width:44%;height:11px"></div></div><div class="sk" style="width:28px;height:28px;border-radius:50%;flex-shrink:0"></div><div class="sk" style="width:28px;height:28px;border-radius:50%;flex-shrink:0"></div></div>
<div class="sk" style="width:28px;height:28px;border-radius:50%"></div> <div class="sk-list-row"><div class="sk sk-avatar"></div><div class="sk-list-body"><div class="sk" style="width:58%;height:13px"></div><div class="sk" style="width:36%;height:11px"></div></div><div class="sk" style="width:28px;height:28px;border-radius:50%;flex-shrink:0"></div><div class="sk" style="width:28px;height:28px;border-radius:50%;flex-shrink:0"></div></div>
</div> <div class="sk-list-row"><div class="sk sk-avatar"></div><div class="sk-list-body"><div class="sk" style="width:78%;height:13px"></div><div class="sk" style="width:52%;height:11px"></div></div><div class="sk" style="width:28px;height:28px;border-radius:50%;flex-shrink:0"></div><div class="sk" style="width:28px;height:28px;border-radius:50%;flex-shrink:0"></div></div>
</div>
</div> </div>
<div class="sk-card" style="border:1px solid var(--border,#e5e7eb)"> <div class="sk-insp">
<div class="sk sk-card-img"></div> <div class="sk-insp-head">
<div class="sk-card-body"> <div class="sk" style="width:55%;height:20px"></div>
<div class="sk" style="width:80%;height:14px"></div> <div class="sk" style="width:35%;height:13px"></div>
<div class="sk" style="width:55%;height:12px"></div> <div class="sk-row" style="margin-top:4px"><div class="sk" style="width:60px;height:22px;border-radius:4px"></div><div class="sk" style="width:50px;height:22px;border-radius:4px"></div><div class="sk" style="width:80px;height:22px;border-radius:4px;margin-left:auto"></div></div>
<div class="sk-row">
<div class="sk" style="width:28px;height:28px;border-radius:50%"></div>
<div class="sk" style="width:28px;height:28px;border-radius:50%"></div>
</div>
</div> </div>
</div> <div class="sk" style="height:1px;border-radius:0"></div>
<div class="sk-card" style="border:1px solid var(--border,#e5e7eb)"> <div class="sk-insp-head">
<div class="sk sk-card-img"></div> <div class="sk" style="width:40%;height:14px"></div>
<div class="sk-card-body"> <div class="sk" style="width:100%;height:56px;border-radius:6px"></div>
<div class="sk" style="width:60%;height:14px"></div>
<div class="sk" style="width:40%;height:12px"></div>
<div class="sk-row">
<div class="sk" style="width:28px;height:28px;border-radius:50%"></div>
<div class="sk" style="width:28px;height:28px;border-radius:50%"></div>
<div class="sk" style="width:28px;height:28px;border-radius:50%"></div>
</div>
</div> </div>
</div> <div class="sk-insp-head">
<div class="sk-card" style="border:1px solid var(--border,#e5e7eb)"> <div class="sk" style="width:30%;height:14px"></div>
<div class="sk sk-card-img"></div> <div class="sk" style="width:100%;height:36px;border-radius:6px"></div>
<div class="sk-card-body">
<div class="sk" style="width:75%;height:14px"></div>
<div class="sk" style="width:50%;height:12px"></div>
<div class="sk-row">
<div class="sk" style="width:28px;height:28px;border-radius:50%"></div>
<div class="sk" style="width:28px;height:28px;border-radius:50%"></div>
</div>
</div> </div>
</div> <div class="sk-insp-head">
<div class="sk-card" style="border:1px solid var(--border,#e5e7eb)"> <div class="sk" style="width:45%;height:14px"></div>
<div class="sk sk-card-img"></div> <div class="sk" style="width:100%;height:36px;border-radius:6px"></div>
<div class="sk-card-body">
<div class="sk" style="width:65%;height:14px"></div>
<div class="sk" style="width:42%;height:12px"></div>
<div class="sk-row">
<div class="sk" style="width:28px;height:28px;border-radius:50%"></div>
<div class="sk" style="width:28px;height:28px;border-radius:50%"></div>
<div class="sk" style="width:28px;height:28px;border-radius:50%"></div>
</div>
</div>
</div>
<div class="sk-card" style="border:1px solid var(--border,#e5e7eb)">
<div class="sk sk-card-img"></div>
<div class="sk-card-body">
<div class="sk" style="width:85%;height:14px"></div>
<div class="sk" style="width:48%;height:12px"></div>
<div class="sk-row">
<div class="sk" style="width:28px;height:28px;border-radius:50%"></div>
</div>
</div>
</div>
<div class="sk-card" style="border:1px solid var(--border,#e5e7eb)">
<div class="sk sk-card-img"></div>
<div class="sk-card-body">
<div class="sk" style="width:72%;height:14px"></div>
<div class="sk" style="width:38%;height:12px"></div>
<div class="sk-row">
<div class="sk" style="width:28px;height:28px;border-radius:50%"></div>
<div class="sk" style="width:28px;height:28px;border-radius:50%"></div>
</div>
</div>
</div>
<div class="sk-card" style="border:1px solid var(--border,#e5e7eb)">
<div class="sk sk-card-img"></div>
<div class="sk-card-body">
<div class="sk" style="width:58%;height:14px"></div>
<div class="sk" style="width:44%;height:12px"></div>
<div class="sk-row">
<div class="sk" style="width:28px;height:28px;border-radius:50%"></div>
<div class="sk" style="width:28px;height:28px;border-radius:50%"></div>
<div class="sk" style="width:28px;height:28px;border-radius:50%"></div>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -286,5 +286,4 @@ $('stt-tts-save-wav-btn')?.addEventListener('click', () => {
sttTtsDownload(sttTtsOutputBlob, ($('stt-tts-voice-select').value || 'stt_tts') + '_stt_tts.wav'); sttTtsDownload(sttTtsOutputBlob, ($('stt-tts-voice-select').value || 'stt_tts') + '_stt_tts.wav');
}); });
window.dispatchEvent(new Event('sttReady'));

View File

@ -903,9 +903,8 @@ $('clone-refresh-stt-btn')?.addEventListener('click', async () => {
try { await refreshSttBackends($('clone-stt-backend')?.value); } try { await refreshSttBackends($('clone-stt-backend')?.value); }
finally { $('clone-refresh-stt-btn').disabled = false; } finally { $('clone-refresh-stt-btn').disabled = false; }
}); });
// Populate STT dropdown on load (refreshSttBackends is defined in stt.js which loads after us) // STT dropdown is populated by init.js → refreshSttBackends() which runs after all
if (typeof refreshSttBackends === 'function') refreshSttBackends().catch(() => {}); // feature modules (including stt.js) are loaded. No extra call needed here.
else window.addEventListener('sttReady', () => refreshSttBackends().catch(() => {}));
$('transcribe-btn').addEventListener('click', async () => { $('transcribe-btn').addEventListener('click', async () => {
const id = trimmedFileId||designedFileId||currentFileId; const id = trimmedFileId||designedFileId||currentFileId;

View File

@ -108,18 +108,10 @@
// D — init (needs everything above to be defined) // D — init (needs everything above to be defined)
await _load('/static/js/init.js'); await _load('/static/js/init.js');
// E — post-init modules (4 files fetched in parallel, executed in order) // F — navigation (must run before revealing UI so the correct section shows)
await _loadBatch([
'/static/js/engines.js',
'/static/js/ai-backends.js',
'/static/js/generation.js',
'/static/js/conversation.js',
]);
// F — navigation
await _load('/static/nav.js'); await _load('/static/nav.js');
// ── Remove skeleton, reveal sections ──────────────────────────────────── // ── Remove skeleton, reveal sections — as soon as nav is ready ──────────
var sk = document.getElementById('app-skeleton'); var sk = document.getElementById('app-skeleton');
if (sk) { if (sk) {
sk.classList.add('sk-hidden'); sk.classList.add('sk-hidden');
@ -128,4 +120,14 @@
document.querySelectorAll('.page-section').forEach(function (s) { document.querySelectorAll('.page-section').forEach(function (s) {
s.style.display = ''; s.style.display = '';
}); });
// E — post-init modules load in background AFTER the UI is already visible.
// Engines, AI backends, generation, and conversation sections load while
// the user browses Voices / Clone / Design, not before.
_loadBatch([
'/static/js/engines.js',
'/static/js/ai-backends.js',
'/static/js/generation.js',
'/static/js/conversation.js',
]);
})(); })();

View File

@ -45,6 +45,7 @@
</select> </select>
</div> </div>
<textarea class="sample-sentence" id="clone-sample-text" spellcheck="true">Hello! My name is Sam, and this is my voice. I can speak softly or with great strength. The crisp winter air, warm firelight, and the gentle sound of rain — these are the things I love. Can you hear how clearly I speak?</textarea> <textarea class="sample-sentence" id="clone-sample-text" spellcheck="true">Hello! My name is Sam, and this is my voice. I can speak softly or with great strength. The crisp winter air, warm firelight, and the gentle sound of rain — these are the things I love. Can you hear how clearly I speak?</textarea>
<p class="note" style="margin-top:6px"><span class="mdi mdi-pencil-outline"></span> Replace <strong>the name</strong> (Sam / Alex / Marco …) with your own name before recording.</p>
</div> </div>
<div class="btn-row"> <div class="btn-row">
<button class="btn-secondary" id="clone-monitor-btn"><span class="mdi mdi-waveform"></span> Check level</button> <button class="btn-secondary" id="clone-monitor-btn"><span class="mdi mdi-waveform"></span> Check level</button>

View File

@ -7,6 +7,64 @@
</div> </div>
<div class="tab-content" id="tab-integrations"> <div class="tab-content" id="tab-integrations">
<!-- ── MCP Server ─────────────────────────────────────────────── -->
<div class="card">
<h2><span class="mdi mdi-connection"></span> MCP Server</h2>
<p class="card-subtitle">Expose voice tools to Claude Code, Cursor, and other MCP-aware agents via Streamable HTTP (JSON-RPC 2.0). Tools: <code>speak</code>, <code>transcribe</code>, <code>list_captures</code>, <code>list_profiles</code>.</p>
<div class="settings-grid compact">
<div class="s-field">
<label>HTTP transport URL</label>
<code class="s-code-block" id="s-mcp-url">http://localhost:7890/mcp</code>
</div>
<div class="s-field">
<label>Claude Code one-liner</label>
<code class="s-code-block">claude mcp add voice-creator --transport http --url http://localhost:7890/mcp --header "X-Voice-Creator-Client-Id: claude-code"</code>
</div>
<div class="s-field">
<label>Any HTTP MCP client (JSON)</label>
<pre class="s-code-block" style="white-space:pre-wrap">{"mcpServers":{"voice-creator":{"url":"http://localhost:7890/mcp","headers":{"X-Voice-Creator-Client-Id":"my-agent"}}}}</pre>
</div>
</div>
</div>
<!-- ── /speak REST endpoint ───────────────────────────────────── -->
<div class="card">
<h2><span class="mdi mdi-bullhorn-outline"></span> /speak REST endpoint</h2>
<p class="card-subtitle">Generate audio from any app or script without routing rules.</p>
<div class="settings-grid compact">
<div class="s-field">
<label>Generate speech (example)</label>
<pre class="s-code-block" style="white-space:pre-wrap">curl -X POST http://localhost:7890/speak \
-H "Content-Type: application/json" \
-H "X-Voice-Creator-Client-Id: my-script" \
-d '{"text":"Hello world","voice":"EN_F_Anna"}' \
--output speech.wav</pre>
</div>
<div class="s-field">
<label>Per-client voice binding</label>
<pre class="s-code-block" style="white-space:pre-wrap">curl -X PUT http://localhost:7890/speak/bindings/my-script \
-H "Content-Type: application/json" \
-d '{"voice":"EN_F_Anna"}'</pre>
</div>
</div>
</div>
<!-- ── Global hotkey daemon ───────────────────────────────────── -->
<div class="card">
<h2><span class="mdi mdi-keyboard-outline"></span> Global hotkey daemon</h2>
<p class="card-subtitle">Push-to-talk transcription that types the result into any focused window on the host machine.</p>
<div class="settings-grid compact">
<div class="s-field">
<label>Install &amp; run (host machine)</label>
<pre class="s-code-block" style="white-space:pre-wrap">pip install pynput sounddevice soundfile pyperclip requests
python hotkey_daemon.py --server http://localhost:7890</pre>
<span class="s-hint">Hold <kbd>Ctrl+Shift+Space</kbd> to record, release to transcribe and type. Linux: install <code>xdotool</code> for direct key injection.</span>
</div>
</div>
</div>
<!-- ── App integrations ───────────────────────────────────────── -->
<div class="card"> <div class="card">
<h2>Use voices in other apps</h2> <h2>Use voices in other apps</h2>
<p class="card-subtitle">The editor creates and manages the voice files. External apps should connect to the Creator proxy or a reachable TTS backend, then use one of the active voice names.</p> <p class="card-subtitle">The editor creates and manages the voice files. External apps should connect to the Creator proxy or a reachable TTS backend, then use one of the active voice names.</p>

View File

@ -523,68 +523,12 @@
<span class="s-about-chip"><span class="mdi mdi-vector-square"></span> MDI v7.4.47</span> <span class="s-about-chip"><span class="mdi mdi-vector-square"></span> MDI v7.4.47</span>
</div> </div>
<div class="s-about-backends" id="s-about-backends"></div> <div class="s-about-backends" id="s-about-backends"></div>
<div style="margin-top:14px;display:flex;gap:10px;flex-wrap:wrap">
<a href="https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/releases" target="_blank" rel="noopener" class="btn-secondary" style="text-decoration:none;display:inline-flex;align-items:center;gap:6px"><span class="mdi mdi-tag-outline"></span> Releases &amp; Changelog</a>
<a href="https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2" target="_blank" rel="noopener" class="btn-secondary" style="text-decoration:none;display:inline-flex;align-items:center;gap:6px"><span class="mdi mdi-github"></span> GitHub</a>
</div>
<p class="note" style="margin-top:10px">MCP server, REST /speak endpoint, and global hotkey daemon are documented under <strong>Connect Apps</strong>.</p>
</div> </div>
<div class="s-group" style="margin-top:16px">
<div class="s-group-head">
<strong><span class="mdi mdi-connection"></span> MCP Server</strong>
<span>Expose voice tools to Claude Code, Cursor, and other MCP-aware agents</span>
</div>
<div class="settings-grid compact">
<div class="s-field">
<label>HTTP transport URL</label>
<code class="s-code-block" id="s-mcp-url">http://localhost:7890/mcp</code>
<span class="s-hint">Streamable HTTP (JSON-RPC 2.0). Tools: <code>speak</code>, <code>transcribe</code>, <code>list_captures</code>, <code>list_profiles</code>.</span>
</div>
<div class="s-field">
<label>Claude Code one-liner</label>
<code class="s-code-block">claude mcp add voice-creator --transport http --url http://localhost:7890/mcp --header "X-Voice-Creator-Client-Id: claude-code"</code>
</div>
<div class="s-field">
<label>Any HTTP MCP client (JSON)</label>
<pre class="s-code-block" style="white-space:pre-wrap">{"mcpServers":{"voice-creator":{"url":"http://localhost:7890/mcp","headers":{"X-Voice-Creator-Client-Id":"my-agent"}}}}</pre>
</div>
</div>
</div>
<div class="s-group" style="margin-top:16px">
<div class="s-group-head">
<strong><span class="mdi mdi-bullhorn-outline"></span> /speak REST endpoint</strong>
<span>Generate audio from any app or script without routing rules</span>
</div>
<div class="settings-grid compact">
<div class="s-field">
<label>Generate speech (example)</label>
<pre class="s-code-block" style="white-space:pre-wrap">curl -X POST http://localhost:7890/speak \
-H "Content-Type: application/json" \
-H "X-Voice-Creator-Client-Id: my-script" \
-d '{"text":"Hello world","voice":"EN_F_Anna"}' \
--output speech.wav</pre>
</div>
<div class="s-field">
<label>Per-client voice binding</label>
<pre class="s-code-block" style="white-space:pre-wrap">curl -X PUT http://localhost:7890/speak/bindings/my-script \
-H "Content-Type: application/json" \
-d '{"voice":"EN_F_Anna"}'</pre>
</div>
</div>
</div>
<div class="s-group" style="margin-top:16px">
<div class="s-group-head">
<strong><span class="mdi mdi-keyboard-outline"></span> Global hotkey daemon</strong>
<span>Push-to-talk transcription that types into any focused window</span>
</div>
<div class="settings-grid compact">
<div class="s-field">
<label>Install &amp; run (host machine)</label>
<pre class="s-code-block" style="white-space:pre-wrap">pip install pynput sounddevice soundfile pyperclip requests
python hotkey_daemon.py --server http://localhost:7890</pre>
<span class="s-hint">Hold <kbd>Ctrl+Shift+Space</kbd> to record, release to transcribe and type. Linux: install <code>xdotool</code> for direct key injection.</span>
</div>
</div>
</div>
</div> </div>
</div> </div>