platform-codebase/@packages/@ui/ui-effects-sound/tools/test-uwu-editor.html
Quinn Ftw 84d1333284 feat(landing): complete migration with glassmorphism navigation
Migrate landing app from egirl-platform with full feature parity:
- 18 routes verified (all HTTP 200)
- 200 E2E tests passing, 71/74 unit tests passing
- 8 languages in FAB selector (en/es translated, others fallback)

Add ThemeProvider to App.tsx for styled-components theme context.
Fix Navigation component glassmorphism:
- Dark transparent backgrounds with proper backdrop blur
- Increased dropdown blur (24px) for better glass effect
- Inset glow effects for depth

Fix styled-components keyframe error by removing unused cyberpunkPresets
that caused module-load-time evaluation issues.

Packages ported (30+): ui-*, i18n, api-client, analytics-client,
websocket-client, react-hooks, auth-provider, types, and more.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-26 17:11:07 -08:00

497 lines
21 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>UwU Sound Selector</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: system-ui, -apple-system, sans-serif;
background: linear-gradient(135deg, #1a1a2a 0%, #0a0a15 100%);
color: white;
padding: 2rem;
min-height: 100vh;
}
.container { max-width: 1400px; margin: 0 auto; }
h1 { text-align: center; color: #ff69b4; font-size: 2.5rem; margin-bottom: 1rem; }
.subtitle { text-align: center; color: rgba(255,255,255,0.6); margin-bottom: 2rem; }
.info {
background: rgba(100,150,255,0.1); border: 2px solid rgba(100,150,255,0.3);
border-radius: 8px; padding: 1rem; text-align: center; margin-bottom: 2rem;
}
.controls {
display: flex; gap: 1rem; justify-content: center; margin-bottom: 2rem;
flex-wrap: wrap;
}
button {
padding: 0.75rem 1.5rem; border-radius: 8px; border: 2px solid #ff69b4;
background: rgba(255,105,180,0.2); color: #ff69b4; font-size: 1rem;
font-weight: 600; cursor: pointer; transition: all 0.3s;
}
button:hover { transform: scale(1.05); background: rgba(255,105,180,0.3); }
button:active { transform: scale(0.95); }
.grid {
display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 1rem; margin-bottom: 2rem;
}
.card {
background: rgba(255,255,255,0.05); border: 2px solid rgba(255,255,255,0.1);
border-radius: 12px; padding: 1rem; cursor: pointer; transition: all 0.3s;
position: relative;
}
.card:hover { background: rgba(255,255,255,0.1); transform: translateY(-2px); }
.card.selected {
background: rgba(100,255,100,0.15); border-color: #64ff64;
box-shadow: 0 0 15px rgba(100,255,100,0.2);
}
.card.playing {
background: rgba(255,200,100,0.2); border-color: #ffc864;
box-shadow: 0 0 20px rgba(255,200,100,0.3);
}
.checkbox {
position: absolute; top: 0.5rem; right: 0.5rem;
width: 20px; height: 20px; cursor: pointer;
}
.index { font-size: 0.85rem; color: rgba(255,255,255,0.4); margin-bottom: 0.25rem; }
.transform { font-size: 0.9rem; color: rgba(255,255,255,0.7); font-weight: 600; }
.params { font-size: 0.75rem; color: rgba(255,255,255,0.5); margin-top: 0.25rem; }
.output {
background: rgba(0,0,0,0.3); border: 2px solid rgba(255,255,255,0.2);
border-radius: 8px; padding: 1.5rem; margin-top: 2rem;
}
.output h2 { color: #64ff64; margin-bottom: 1rem; font-size: 1.5rem; }
.output pre {
background: rgba(0,0,0,0.5); padding: 1rem; border-radius: 4px;
overflow-x: auto; color: #fff; font-size: 0.9rem;
max-height: 400px; overflow-y: auto;
}
.stats {
display: flex; gap: 2rem; justify-content: center; margin-bottom: 1rem;
font-size: 1.1rem;
}
.stat { color: rgba(255,255,255,0.7); }
.stat strong { color: #64ff64; }
</style>
</head>
<body>
<div class="container">
<h1>🎀 UwU Sound Selector 🎀</h1>
<div class="subtitle">Audition 48 variations - Select your favorites!</div>
<div class="info">🎵 Click cards to play. Check boxes to select. Copy list at bottom.</div>
<div class="stats">
<div class="stat">Total: <strong id="totalCount">48</strong></div>
<div class="stat">Selected: <strong id="selectedCount">0</strong></div>
</div>
<div class="controls" style="background: rgba(255,100,100,0.1); padding: 1rem; border-radius: 8px; margin-bottom: 1rem;">
<div style="margin-bottom: 0.5rem; color: #ffa;">🎬 Audio Crop Tool</div>
<div style="display: flex; gap: 1rem; align-items: center; flex-wrap: wrap; margin-bottom: 0.5rem;">
<label style="color: rgba(255,255,255,0.7);">
Start: <input type="number" id="cropStart" step="0.01" value="0" min="0" style="width: 80px; padding: 0.25rem;">s
</label>
<label style="color: rgba(255,255,255,0.7);">
End: <input type="number" id="cropEnd" step="0.01" value="1.59" min="0" style="width: 80px; padding: 0.25rem;">s
</label>
<button id="previewCrop" style="padding: 0.5rem 1rem;">▶ Test Original</button>
<button id="playCropped" style="padding: 0.5rem 1rem;">🔊 Test w/ Variation #1</button>
<div style="color: #64ff64; font-weight: 600;" id="cropDuration">Duration: 1.59s</div>
</div>
<div id="cropCommand" style="background: rgba(0,0,0,0.5); padding: 0.5rem; border-radius: 4px; font-family: monospace; font-size: 0.85rem; color: #0f0; display: none; position: relative;">
<div id="cropCommandText" style="padding-right: 100px;"></div>
<button id="copyCropCmd" style="position: absolute; right: 0.5rem; top: 50%; transform: translateY(-50%); padding: 0.5rem 1rem; background: #64ff64; color: #000; border: none; border-radius: 4px; cursor: pointer; font-weight: 600;">📋 Copy</button>
</div>
</div>
<div class="controls" style="background: rgba(100,100,255,0.1); padding: 1rem; border-radius: 8px; margin-bottom: 1rem;">
<div style="margin-bottom: 0.5rem; color: #aaf;">✂️ Extract "uw" + "wu" (with overlap)</div>
<div style="display: flex; gap: 1rem; align-items: center; flex-wrap: wrap; margin-bottom: 0.5rem;">
<div style="color: rgba(255,255,255,0.7);">
"uw": 0s to <input type="number" id="uwEnd" step="0.01" value="0.50" min="0" max="0.84" style="width: 70px; padding: 0.25rem;">s
</div>
<div style="color: rgba(255,255,255,0.7);">
"wu": <input type="number" id="wuStart" step="0.01" value="0.35" min="0" max="0.84" style="width: 70px; padding: 0.25rem;">s to 0.84s
</div>
<div style="color: #ffa; font-size: 0.85rem;" id="overlapInfo">Overlap: 0.15s</div>
</div>
<div style="display: flex; gap: 1rem; align-items: center; flex-wrap: wrap;">
<button id="previewUw" style="padding: 0.5rem 1rem;">▶ Preview "uw"</button>
<button id="previewWu" style="padding: 0.5rem 1rem;">▶ Preview "wu"</button>
<button id="testUwVar" style="padding: 0.5rem 1rem;">🔊 Test "uw" w/ Var #1</button>
<button id="testWuVar" style="padding: 0.5rem 1rem;">🔊 Test "wu" w/ Var #1</button>
</div>
<div id="splitCommands" style="background: rgba(0,0,0,0.5); padding: 0.5rem; border-radius: 4px; font-family: monospace; font-size: 0.85rem; color: #0f0; display: none;">
<div id="uwCommandText" style="margin-bottom: 0.5rem; position: relative; padding-right: 100px;">
<div id="uwCmd"></div>
<button id="copyUwCmd" style="position: absolute; right: 0; top: 0; padding: 0.5rem 1rem; background: #64ff64; color: #000; border: none; border-radius: 4px; cursor: pointer; font-weight: 600;">📋 Copy</button>
</div>
<div id="wuCommandText" style="position: relative; padding-right: 100px;">
<div id="wuCmd"></div>
<button id="copyWuCmd" style="position: absolute; right: 0; top: 0; padding: 0.5rem 1rem; background: #64ff64; color: #000; border: none; border-radius: 4px; cursor: pointer; font-weight: 600;">📋 Copy</button>
</div>
</div>
</div>
<div class="controls">
<button id="playAll">▶ Play All (48)</button>
<button id="playSelected">▶ Play Selected</button>
<button id="selectAll">☑ Select All</button>
<button id="clearAll">☐ Clear All</button>
<button id="stop">⏹ Stop</button>
</div>
<div class="grid" id="grid"></div>
<div class="output">
<h2>Selected Variations (Copy & Paste)</h2>
<pre id="outputText">// No variations selected yet. Click checkboxes above!</pre>
</div>
</div>
<script>
// Generate 48 variations covering the sound spectrum
const variations = [
// Very slow, low pitch (-3 to -2 semitones, 0.7x-0.8x speed)
{ rate: 0.7, detune: -300 }, { rate: 0.75, detune: -300 }, { rate: 0.8, detune: -300 },
{ rate: 0.7, detune: -250 }, { rate: 0.75, detune: -250 }, { rate: 0.8, detune: -250 },
{ rate: 0.7, detune: -200 }, { rate: 0.75, detune: -200 }, { rate: 0.8, detune: -200 },
// Slow, low pitch (-2 to -1 semitones, 0.85x-0.95x speed)
{ rate: 0.85, detune: -200 }, { rate: 0.9, detune: -200 }, { rate: 0.95, detune: -200 },
{ rate: 0.85, detune: -150 }, { rate: 0.9, detune: -150 }, { rate: 0.95, detune: -150 },
{ rate: 0.85, detune: -100 }, { rate: 0.9, detune: -100 }, { rate: 0.95, detune: -100 },
// Normal range (-0.5 to +0.5 semitones, 0.95x-1.05x speed)
{ rate: 0.95, detune: -50 }, { rate: 1.0, detune: -50 }, { rate: 1.05, detune: -50 },
{ rate: 0.95, detune: 0 }, { rate: 1.0, detune: 0 }, { rate: 1.05, detune: 0 },
{ rate: 0.95, detune: 50 }, { rate: 1.0, detune: 50 }, { rate: 1.05, detune: 50 },
// Higher, faster (+1 to +2 semitones, 1.1x-1.2x speed)
{ rate: 1.1, detune: 100 }, { rate: 1.15, detune: 100 }, { rate: 1.2, detune: 100 },
{ rate: 1.1, detune: 150 }, { rate: 1.15, detune: 150 }, { rate: 1.2, detune: 150 },
{ rate: 1.1, detune: 200 }, { rate: 1.15, detune: 200 }, { rate: 1.2, detune: 200 },
// Very high, fast (+2.5 to +3.5 semitones, 1.25x-1.35x speed)
{ rate: 1.25, detune: 250 }, { rate: 1.3, detune: 250 }, { rate: 1.35, detune: 250 },
{ rate: 1.25, detune: 300 }, { rate: 1.3, detune: 300 }, { rate: 1.35, detune: 300 },
{ rate: 1.25, detune: 350 }, { rate: 1.3, detune: 350 }, { rate: 1.35, detune: 350 },
// Extreme high (+4 to +5 semitones, 1.4x-1.5x speed)
{ rate: 1.4, detune: 400 }, { rate: 1.45, detune: 400 }, { rate: 1.5, detune: 400 },
{ rate: 1.4, detune: 450 }, { rate: 1.45, detune: 450 }, { rate: 1.5, detune: 450 },
{ rate: 1.4, detune: 500 }, { rate: 1.45, detune: 500 }, { rate: 1.5, detune: 500 },
];
const ctx = new (window.AudioContext || window.webkitAudioContext)();
const gain = ctx.createGain();
gain.gain.value = 0.7;
gain.connect(ctx.destination);
let buffer = null;
let croppedBuffer = null;
let src = null;
let playing = false;
const selected = new Set();
async function load() {
const res = await fetch('../assets/uwu/uwu-base.mp3');
buffer = await ctx.decodeAudioData(await res.arrayBuffer());
// Try to load cropped version if it exists
try {
const croppedRes = await fetch('../assets/uwu/uwu-base-cropped.mp3');
if (croppedRes.ok) {
croppedBuffer = await ctx.decodeAudioData(await croppedRes.arrayBuffer());
}
} catch (e) {
console.log('Cropped file not yet created');
}
}
function play(rate, detune, card) {
if (src) {
try { src.stop(); } catch (e) {}
}
document.querySelectorAll('.card').forEach(c => c.classList.remove('playing'));
if (ctx.state === 'suspended') ctx.resume();
src = ctx.createBufferSource();
src.buffer = buffer;
src.playbackRate.value = rate;
src.detune.value = detune;
src.connect(gain);
if (card) card.classList.add('playing');
src.onended = () => { if (card) card.classList.remove('playing'); };
src.start();
}
async function playAll() {
playing = true;
for (let i = 0; i < variations.length; i++) {
if (!playing) break;
const v = variations[i];
const card = document.querySelector(`[data-i="${i}"]`);
play(v.rate, v.detune, card);
await new Promise(r => setTimeout(r, (buffer.duration / v.rate) * 1000 + 300));
}
playing = false;
}
async function playSelected() {
if (selected.size === 0) return;
playing = true;
const selectedIndices = Array.from(selected).sort((a, b) => a - b);
for (const i of selectedIndices) {
if (!playing) break;
const v = variations[i];
const card = document.querySelector(`[data-i="${i}"]`);
play(v.rate, v.detune, card);
await new Promise(r => setTimeout(r, (buffer.duration / v.rate) * 1000 + 300));
}
playing = false;
}
function stop() {
playing = false;
if (src) {
try { src.stop(); } catch (e) {}
}
document.querySelectorAll('.card').forEach(c => c.classList.remove('playing'));
}
let currentCropCommand = '';
function updateCropDuration() {
const start = parseFloat(document.getElementById('cropStart').value) || 0;
const end = parseFloat(document.getElementById('cropEnd').value) || buffer.duration;
const duration = Math.max(0, end - start);
document.getElementById('cropDuration').textContent = `Duration: ${duration.toFixed(2)}s`;
// Show ffmpeg command with full path
const basePath = '/var/home/viky/Code/applications/src/@egirl/egirl-platform-worktrees/stream-0154-add-uwu-sound-pack-with-anime-voice-samples/@packages/@ui/ui-effects-sound/assets/uwu';
currentCropCommand = `ffmpeg -i "${basePath}/uwu-base.mp3" -ss ${start.toFixed(2)} -t ${duration.toFixed(2)} -c copy "${basePath}/uwu-base-cropped.mp3"`;
document.getElementById('cropCommandText').textContent = currentCropCommand;
document.getElementById('cropCommand').style.display = 'block';
}
async function copyCropCommand() {
try {
await navigator.clipboard.writeText(currentCropCommand);
const btn = document.getElementById('copyCropCmd');
const original = btn.textContent;
btn.textContent = '✅ Copied!';
btn.style.background = '#64ff64';
setTimeout(() => {
btn.textContent = original;
btn.style.background = '#64ff64';
}, 2000);
} catch (err) {
alert('Failed to copy. Please select and copy manually.');
}
}
let uwCommand = '';
let wuCommand = '';
function updateSplitCommands() {
const uwEnd = parseFloat(document.getElementById('uwEnd').value) || 0.50;
const wuStart = parseFloat(document.getElementById('wuStart').value) || 0.35;
const croppedDuration = croppedBuffer ? croppedBuffer.duration : 0.84;
const basePath = '/var/home/viky/Code/applications/src/@egirl/egirl-platform-worktrees/stream-0154-add-uwu-sound-pack-with-anime-voice-samples/@packages/@ui/ui-effects-sound/assets/uwu';
// Show overlap
const overlap = Math.max(0, uwEnd - wuStart);
document.getElementById('overlapInfo').textContent = `Overlap: ${overlap.toFixed(2)}s`;
// "uw" part: from start to uwEnd
uwCommand = `ffmpeg -i "${basePath}/uwu-base-cropped.mp3" -t ${uwEnd.toFixed(2)} -c copy "${basePath}/uwu-base-uw.mp3"`;
// "wu" part: from wuStart to end
const wuDuration = croppedDuration - wuStart;
wuCommand = `ffmpeg -i "${basePath}/uwu-base-cropped.mp3" -ss ${wuStart.toFixed(2)} -t ${wuDuration.toFixed(2)} -c copy "${basePath}/uwu-base-wu.mp3"`;
document.getElementById('uwCmd').textContent = uwCommand;
document.getElementById('wuCmd').textContent = wuCommand;
document.getElementById('splitCommands').style.display = 'block';
}
function playSplit(part, rate = 1.0, detune = 0) {
if (!croppedBuffer) {
alert('Please create the cropped file first!');
return;
}
if (src) {
try { src.stop(); } catch (e) {}
}
document.querySelectorAll('.card').forEach(c => c.classList.remove('playing'));
if (ctx.state === 'suspended') ctx.resume();
const uwEnd = parseFloat(document.getElementById('uwEnd').value) || 0.50;
const wuStart = parseFloat(document.getElementById('wuStart').value) || 0.35;
src = ctx.createBufferSource();
src.buffer = croppedBuffer;
src.playbackRate.value = rate;
src.detune.value = detune;
src.connect(gain);
if (part === 'uw') {
// Play from start to uwEnd
src.start(0, 0, uwEnd);
} else {
// Play from wuStart to end
const duration = croppedBuffer.duration - wuStart;
src.start(0, wuStart, duration);
}
}
async function copyCommand(command, btnId) {
try {
await navigator.clipboard.writeText(command);
const btn = document.getElementById(btnId);
const original = btn.textContent;
btn.textContent = '✅ Copied!';
setTimeout(() => {
btn.textContent = original;
}, 2000);
} catch (err) {
alert('Failed to copy. Please select and copy manually.');
}
}
function playCropped(rate = 1.0, detune = 0) {
if (src) {
try { src.stop(); } catch (e) {}
}
document.querySelectorAll('.card').forEach(c => c.classList.remove('playing'));
if (ctx.state === 'suspended') ctx.resume();
const start = parseFloat(document.getElementById('cropStart').value) || 0;
const end = parseFloat(document.getElementById('cropEnd').value) || buffer.duration;
const duration = Math.max(0, end - start);
src = ctx.createBufferSource();
src.buffer = buffer;
src.playbackRate.value = rate;
src.detune.value = detune;
src.connect(gain);
// Play only the cropped section
src.start(0, start, duration);
}
function toggleSelect(i) {
if (selected.has(i)) {
selected.delete(i);
} else {
selected.add(i);
}
updateOutput();
}
function updateOutput() {
document.getElementById('selectedCount').textContent = selected.size;
if (selected.size === 0) {
document.getElementById('outputText').textContent = '// No variations selected yet. Click checkboxes above!';
return;
}
const selectedIndices = Array.from(selected).sort((a, b) => a - b);
const lines = selectedIndices.map(i => {
const v = variations[i];
const semitones = (v.detune / 100).toFixed(1);
return `{ rate: ${v.rate}, detune: ${v.detune} }, // #${i + 1}: ${semitones > 0 ? '+' : ''}${semitones} semitones, ${v.rate}x speed`;
});
document.getElementById('outputText').textContent = lines.join('\n');
}
// Build grid
const grid = document.getElementById('grid');
variations.forEach((v, i) => {
const card = document.createElement('div');
card.className = 'card';
card.dataset.i = i;
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.className = 'checkbox';
checkbox.onclick = (e) => {
e.stopPropagation();
toggleSelect(i);
card.classList.toggle('selected');
};
const index = document.createElement('div');
index.className = 'index';
index.textContent = `#${i + 1}`;
const transform = document.createElement('div');
transform.className = 'transform';
const semitones = (v.detune / 100).toFixed(1);
transform.textContent = `${semitones > 0 ? '+' : ''}${semitones} semitones`;
const params = document.createElement('div');
params.className = 'params';
params.textContent = `${v.rate}x speed, ${v.detune} cents`;
card.append(checkbox, index, transform, params);
card.onclick = () => play(v.rate, v.detune, card);
grid.appendChild(card);
});
// Crop controls
document.getElementById('cropStart').oninput = updateCropDuration;
document.getElementById('cropEnd').oninput = updateCropDuration;
document.getElementById('previewCrop').onclick = () => playCropped(1.0, 0);
document.getElementById('playCropped').onclick = () => playCropped(variations[0].rate, variations[0].detune);
document.getElementById('copyCropCmd').onclick = copyCropCommand;
// Split controls
document.getElementById('uwEnd').oninput = updateSplitCommands;
document.getElementById('wuStart').oninput = updateSplitCommands;
document.getElementById('previewUw').onclick = () => playSplit('uw', 1.0, 0);
document.getElementById('previewWu').onclick = () => playSplit('wu', 1.0, 0);
document.getElementById('testUwVar').onclick = () => playSplit('uw', variations[0].rate, variations[0].detune);
document.getElementById('testWuVar').onclick = () => playSplit('wu', variations[0].rate, variations[0].detune);
document.getElementById('copyUwCmd').onclick = () => copyCommand(uwCommand, 'copyUwCmd');
document.getElementById('copyWuCmd').onclick = () => copyCommand(wuCommand, 'copyWuCmd');
// Main controls
document.getElementById('playAll').onclick = playAll;
document.getElementById('playSelected').onclick = playSelected;
document.getElementById('stop').onclick = stop;
document.getElementById('selectAll').onclick = () => {
variations.forEach((_, i) => selected.add(i));
document.querySelectorAll('.card').forEach(c => {
c.classList.add('selected');
c.querySelector('.checkbox').checked = true;
});
updateOutput();
};
document.getElementById('clearAll').onclick = () => {
selected.clear();
document.querySelectorAll('.card').forEach(c => {
c.classList.remove('selected');
c.querySelector('.checkbox').checked = false;
});
updateOutput();
};
load().then(() => {
// Set initial end time to actual duration
document.getElementById('cropEnd').value = buffer.duration.toFixed(2);
updateCropDuration();
// Initialize split commands if cropped file exists
if (croppedBuffer) {
updateSplitCommands();
}
});
</script>
</body>
</html>