Merge carrier type toggle into Step 1 accordion, reduce to 3 steps

Remove the dedicated Carrier Type accordion step and merge the
Image/Audio toggle into the Carrier & Mode step. The toggle now sits
in a two-column row aligned with the embedding mode buttons. Steps
renumbered from 4 to 3, carrier label changed to "Carrier File",
mode hint updates on carrier type switch via dispatched change events.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
adlee-was-taken
2026-03-01 16:27:02 -05:00
parent ef5a9ce9cb
commit 05382c4081

View File

@@ -130,58 +130,23 @@
<div class="accordion step-accordion" id="encodeAccordion">
<!-- ================================================================
STEP 1: CARRIER TYPE (v4.3.0)
================================================================ -->
<div class="accordion-item" id="carrierTypeStep">
<h2 class="accordion-header">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#stepCarrierType">
<span class="step-title">
<span class="step-number" id="stepCarrierTypeNumber">1</span>
<i class="bi bi-collection me-1"></i> Carrier Type
</span>
<span class="step-summary" id="stepCarrierTypeSummary"></span>
</button>
</h2>
<div id="stepCarrierType" class="accordion-collapse collapse show" data-bs-parent="#encodeAccordion">
<div class="accordion-body">
<input type="hidden" name="carrier_type" id="carrierTypeInput" value="image">
<div class="btn-group w-100" role="group">
<input type="radio" class="btn-check" name="carrier_type_select" id="typeImage" value="image" checked>
<label class="btn btn-outline-secondary" for="typeImage">
<i class="bi bi-image me-1"></i> Image
</label>
<input type="radio" class="btn-check" name="carrier_type_select" id="typeAudio" value="audio"
{% if not has_audio %}disabled{% endif %}>
<label class="btn btn-outline-secondary {% if not has_audio %}disabled text-muted{% endif %}" for="typeAudio">
<i class="bi bi-music-note-beamed me-1"></i> Audio
{% if not has_audio %}<small class="d-block" style="font-size: 0.65rem;">(not available)</small>{% endif %}
</label>
</div>
{% if not has_audio %}
<div class="form-text text-warning mt-2">
<i class="bi bi-exclamation-triangle me-1"></i>Audio requires numpy and soundfile packages
</div>
{% endif %}
</div>
</div>
</div>
<!-- ================================================================
STEP 2: IMAGES & MODE
STEP 1: CARRIER & MODE
================================================================ -->
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#stepImages">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#stepImages">
<span class="step-title">
<span class="step-number" id="stepImagesNumber">2</span>
<i class="bi bi-images me-1"></i> Reference, Carrier, Mode
<span class="step-number" id="stepImagesNumber">1</span>
<i class="bi bi-images me-1"></i> Carrier & Mode
</span>
<span class="step-summary" id="stepImagesSummary">Select reference & carrier</span>
</button>
</h2>
<div id="stepImages" class="accordion-collapse collapse" data-bs-parent="#encodeAccordion">
<div id="stepImages" class="accordion-collapse collapse show" data-bs-parent="#encodeAccordion">
<div class="accordion-body">
<input type="hidden" name="carrier_type" id="carrierTypeInput" value="image">
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">
@@ -208,10 +173,10 @@
</div>
<div class="col-md-6 mb-3">
<div id="imageCarrierSection">
<label class="form-label">
<i class="bi bi-file-earmark-image me-1"></i> Carrier Image
<i class="bi bi-file-earmark me-1"></i> Carrier File
</label>
<div id="imageCarrierSection">
<div class="drop-zone pixel-container" id="carrierDropZone">
<input type="file" name="carrier" accept="image/*" required id="carrierInput">
<div class="drop-zone-label">
@@ -231,16 +196,13 @@
<div class="pixel-dimensions" id="carrierDims">-- x -- px</div>
</div>
</div>
<div class="form-text">Image to hide your message in</div>
<div class="form-text" id="imageCarrierHint">Image to hide your message in</div>
</div>
<!-- Audio Carrier (hidden by default, shown when audio type selected) -->
<div class="d-none" id="audioCarrierSection">
<label class="form-label">
<i class="bi bi-file-earmark-music me-1"></i> Carrier Audio
</label>
<div class="drop-zone pixel-container" id="audioCarrierDropZone">
<input type="file" name="carrier" accept="audio/*" id="audioCarrierInput">
<input type="file" name="audio_carrier" accept="audio/*" id="audioCarrierInput">
<div class="drop-zone-label">
<i class="bi bi-music-note-beamed fs-3 d-block mb-2 text-muted"></i>
<span class="text-muted">Drop audio or click</span>
@@ -251,7 +213,7 @@
<div class="pixel-dimensions" id="audioCarrierDuration">--:-- duration</div>
</div>
</div>
<div class="form-text">Audio file to hide your message in</div>
<div class="form-text" id="audioCarrierHint">Audio file to hide your message in</div>
</div>
</div>
</div>
@@ -278,7 +240,9 @@
</div>
</div>
<!-- Embedding Mode (compact inline) -->
<!-- Mode & Carrier Type toggles (aligned row) -->
<div class="row">
<div class="col-md-6">
<div id="imageModeGroup">
<div class="d-flex gap-2 align-items-center flex-wrap mb-2">
<div class="btn-group btn-group-sm" role="group">
@@ -304,33 +268,46 @@
</span>
</div>
</div>
<!-- Audio Modes (hidden by default) -->
<div class="d-none" id="audioModeGroup">
<div class="btn-group btn-group-sm" role="group">
<div class="btn-group btn-group-sm mb-2" role="group">
<input type="radio" class="btn-check" name="embed_mode" id="modeAudioLsb" value="audio_lsb">
<label class="btn btn-outline-secondary btn-sm text-nowrap" for="modeAudioLsb"><i class="bi bi-grid-3x3-gap me-1"></i>LSB</label>
<input type="radio" class="btn-check" name="embed_mode" id="modeAudioSpread" value="audio_spread">
<label class="btn btn-outline-secondary btn-sm text-nowrap" for="modeAudioSpread"><i class="bi bi-broadcast me-1"></i>Spread</label>
</div>
</div>
<div class="form-text" id="modeHint">
<i class="bi bi-{% if has_dct %}phone{% else %}hdd{% endif %} me-1"></i>{% if has_dct %}Survives social media compression{% else %}Higher capacity for direct transfers{% endif %}
</div>
</div>
<div class="col-md-6">
<div class="d-flex align-items-center gap-2">
<div class="btn-group btn-group-sm" role="group">
<input type="radio" class="btn-check" name="carrier_type_select" id="typeImage" value="image" checked>
<label class="btn btn-outline-secondary btn-sm text-nowrap" for="typeImage"><i class="bi bi-image me-1"></i>Image</label>
<input type="radio" class="btn-check" name="carrier_type_select" id="typeAudio" value="audio" {% if not has_audio %}disabled{% endif %}>
<label class="btn btn-outline-secondary btn-sm text-nowrap {% if not has_audio %}disabled text-muted{% endif %}" for="typeAudio"><i class="bi bi-music-note-beamed me-1"></i>Audio</label>
</div>
{% if not has_audio %}
<span class="form-text text-warning mb-0" style="font-size: 0.7rem;"><i class="bi bi-exclamation-triangle me-1"></i>Requires numpy + soundfile</span>
{% endif %}
</div>
</div>
</div>
</div>
</div>
</div>
<!-- ================================================================
STEP 3: PAYLOAD
STEP 2: PAYLOAD
================================================================ -->
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#stepPayload">
<span class="step-title">
<span class="step-number" id="stepPayloadNumber">3</span>
<span class="step-number" id="stepPayloadNumber">2</span>
<i class="bi bi-box me-1"></i> Payload
</span>
<span class="step-summary" id="stepPayloadSummary">Message or file to hide</span>
@@ -382,13 +359,13 @@
</div>
<!-- ================================================================
STEP 4: SECURITY
STEP 3: SECURITY
================================================================ -->
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#stepSecurity">
<span class="step-title">
<span class="step-number" id="stepSecurityNumber">4</span>
<span class="step-number" id="stepSecurityNumber">3</span>
<i class="bi bi-shield-lock me-1"></i> Security
</span>
<span class="step-summary" id="stepSecuritySummary">Passphrase & keys</span>
@@ -536,7 +513,9 @@
// ============================================================================
const modeHints = {
dct: { icon: 'phone', text: 'Survives social media compression' },
lsb: { icon: 'hdd', text: 'Higher capacity, outputs Color PNG' }
lsb: { icon: 'hdd', text: 'Higher capacity, outputs Color PNG' },
audio_lsb: { icon: 'soundwave', text: 'Highest capacity, lossless carriers only (WAV/FLAC)' },
audio_spread: { icon: 'broadcast', text: 'Lower capacity, survives lossy conversion (MP3/AAC)' }
};
document.querySelectorAll('input[name="embed_mode"]').forEach(radio => {
@@ -561,7 +540,6 @@ const imageModeGroup = document.getElementById('imageModeGroup');
const audioModeGroup = document.getElementById('audioModeGroup');
const capacityPanel = document.getElementById('capacityPanel');
const audioCapacityPanel = document.getElementById('audioCapacityPanel');
const stepCarrierTypeSummary = document.getElementById('stepCarrierTypeSummary');
carrierTypeRadios.forEach(radio => {
radio.addEventListener('change', function() {
@@ -586,23 +564,18 @@ carrierTypeRadios.forEach(radio => {
if (capacityPanel) capacityPanel.classList.add('d-none');
if (audioCapacityPanel) audioCapacityPanel.classList.add('d-none');
// Update summary
if (stepCarrierTypeSummary) {
stepCarrierTypeSummary.textContent = isAudio ? 'Audio' : 'Image';
}
// Select default mode for the active type
// Select default mode for the active type and update hint
if (isAudio) {
const audioLsb = document.getElementById('modeAudioLsb');
if (audioLsb) audioLsb.checked = true;
if (audioLsb) { audioLsb.checked = true; audioLsb.dispatchEvent(new Event('change')); }
} else {
// Reset to DCT if available, else LSB
const dctRadio = document.getElementById('modeDct');
const lsbRadio = document.getElementById('modeLsb');
if (dctRadio && !dctRadio.disabled) {
dctRadio.checked = true;
dctRadio.checked = true; dctRadio.dispatchEvent(new Event('change'));
} else if (lsbRadio) {
lsbRadio.checked = true;
lsbRadio.checked = true; lsbRadio.dispatchEvent(new Event('change'));
}
}
@@ -689,12 +662,12 @@ function updateImagesSummary() {
summary.textContent = ref ? ref.name.slice(0, 15) : carrier.name.slice(0, 15);
summary.classList.remove('has-content');
stepNum.classList.remove('complete');
stepNum.textContent = '2';
stepNum.textContent = '1';
} else {
summary.textContent = isAudio ? 'Select reference & audio' : 'Select reference & carrier';
summary.classList.remove('has-content');
stepNum.classList.remove('complete');
stepNum.textContent = '2';
stepNum.textContent = '1';
}
}
@@ -720,7 +693,7 @@ function updatePayloadSummary() {
summary.textContent = 'Message or file to hide';
summary.classList.remove('has-content');
stepNum.classList.remove('complete');
stepNum.textContent = '3';
stepNum.textContent = '2';
}
}
@@ -748,7 +721,7 @@ function updateSecuritySummary() {
summary.textContent = 'Passphrase & keys';
summary.classList.remove('has-content');
stepNum.classList.remove('complete');
stepNum.textContent = '4';
stepNum.textContent = '3';
}
}