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:
@@ -130,58 +130,23 @@
|
|||||||
<div class="accordion step-accordion" id="encodeAccordion">
|
<div class="accordion step-accordion" id="encodeAccordion">
|
||||||
|
|
||||||
<!-- ================================================================
|
<!-- ================================================================
|
||||||
STEP 1: CARRIER TYPE (v4.3.0)
|
STEP 1: CARRIER & MODE
|
||||||
================================================================ -->
|
|
||||||
<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
|
|
||||||
================================================================ -->
|
================================================================ -->
|
||||||
<div class="accordion-item">
|
<div class="accordion-item">
|
||||||
<h2 class="accordion-header">
|
<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-title">
|
||||||
<span class="step-number" id="stepImagesNumber">2</span>
|
<span class="step-number" id="stepImagesNumber">1</span>
|
||||||
<i class="bi bi-images me-1"></i> Reference, Carrier, Mode
|
<i class="bi bi-images me-1"></i> Carrier & Mode
|
||||||
</span>
|
</span>
|
||||||
<span class="step-summary" id="stepImagesSummary">Select reference & carrier</span>
|
<span class="step-summary" id="stepImagesSummary">Select reference & carrier</span>
|
||||||
</button>
|
</button>
|
||||||
</h2>
|
</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">
|
<div class="accordion-body">
|
||||||
|
|
||||||
|
<input type="hidden" name="carrier_type" id="carrierTypeInput" value="image">
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6 mb-3">
|
<div class="col-md-6 mb-3">
|
||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
@@ -208,10 +173,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-6 mb-3">
|
<div class="col-md-6 mb-3">
|
||||||
<div id="imageCarrierSection">
|
|
||||||
<label class="form-label">
|
<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>
|
</label>
|
||||||
|
<div id="imageCarrierSection">
|
||||||
<div class="drop-zone pixel-container" id="carrierDropZone">
|
<div class="drop-zone pixel-container" id="carrierDropZone">
|
||||||
<input type="file" name="carrier" accept="image/*" required id="carrierInput">
|
<input type="file" name="carrier" accept="image/*" required id="carrierInput">
|
||||||
<div class="drop-zone-label">
|
<div class="drop-zone-label">
|
||||||
@@ -231,16 +196,13 @@
|
|||||||
<div class="pixel-dimensions" id="carrierDims">-- x -- px</div>
|
<div class="pixel-dimensions" id="carrierDims">-- x -- px</div>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
|
|
||||||
<!-- Audio Carrier (hidden by default, shown when audio type selected) -->
|
<!-- Audio Carrier (hidden by default, shown when audio type selected) -->
|
||||||
<div class="d-none" id="audioCarrierSection">
|
<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">
|
<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">
|
<div class="drop-zone-label">
|
||||||
<i class="bi bi-music-note-beamed fs-3 d-block mb-2 text-muted"></i>
|
<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>
|
<span class="text-muted">Drop audio or click</span>
|
||||||
@@ -251,7 +213,7 @@
|
|||||||
<div class="pixel-dimensions" id="audioCarrierDuration">--:-- duration</div>
|
<div class="pixel-dimensions" id="audioCarrierDuration">--:-- duration</div>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -278,7 +240,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Embedding Mode (compact inline) -->
|
<!-- Mode & Carrier Type toggles (aligned row) -->
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
<div id="imageModeGroup">
|
<div id="imageModeGroup">
|
||||||
<div class="d-flex gap-2 align-items-center flex-wrap mb-2">
|
<div class="d-flex gap-2 align-items-center flex-wrap mb-2">
|
||||||
<div class="btn-group btn-group-sm" role="group">
|
<div class="btn-group btn-group-sm" role="group">
|
||||||
@@ -304,33 +268,46 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Audio Modes (hidden by default) -->
|
<!-- Audio Modes (hidden by default) -->
|
||||||
<div class="d-none" id="audioModeGroup">
|
<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">
|
<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>
|
<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">
|
<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>
|
<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>
|
</div>
|
||||||
|
|
||||||
<div class="form-text" id="modeHint">
|
<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 %}
|
<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>
|
||||||
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ================================================================
|
<!-- ================================================================
|
||||||
STEP 3: PAYLOAD
|
STEP 2: PAYLOAD
|
||||||
================================================================ -->
|
================================================================ -->
|
||||||
<div class="accordion-item">
|
<div class="accordion-item">
|
||||||
<h2 class="accordion-header">
|
<h2 class="accordion-header">
|
||||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#stepPayload">
|
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#stepPayload">
|
||||||
<span class="step-title">
|
<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
|
<i class="bi bi-box me-1"></i> Payload
|
||||||
</span>
|
</span>
|
||||||
<span class="step-summary" id="stepPayloadSummary">Message or file to hide</span>
|
<span class="step-summary" id="stepPayloadSummary">Message or file to hide</span>
|
||||||
@@ -382,13 +359,13 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ================================================================
|
<!-- ================================================================
|
||||||
STEP 4: SECURITY
|
STEP 3: SECURITY
|
||||||
================================================================ -->
|
================================================================ -->
|
||||||
<div class="accordion-item">
|
<div class="accordion-item">
|
||||||
<h2 class="accordion-header">
|
<h2 class="accordion-header">
|
||||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#stepSecurity">
|
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#stepSecurity">
|
||||||
<span class="step-title">
|
<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
|
<i class="bi bi-shield-lock me-1"></i> Security
|
||||||
</span>
|
</span>
|
||||||
<span class="step-summary" id="stepSecuritySummary">Passphrase & keys</span>
|
<span class="step-summary" id="stepSecuritySummary">Passphrase & keys</span>
|
||||||
@@ -536,7 +513,9 @@
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
const modeHints = {
|
const modeHints = {
|
||||||
dct: { icon: 'phone', text: 'Survives social media compression' },
|
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 => {
|
document.querySelectorAll('input[name="embed_mode"]').forEach(radio => {
|
||||||
@@ -561,7 +540,6 @@ const imageModeGroup = document.getElementById('imageModeGroup');
|
|||||||
const audioModeGroup = document.getElementById('audioModeGroup');
|
const audioModeGroup = document.getElementById('audioModeGroup');
|
||||||
const capacityPanel = document.getElementById('capacityPanel');
|
const capacityPanel = document.getElementById('capacityPanel');
|
||||||
const audioCapacityPanel = document.getElementById('audioCapacityPanel');
|
const audioCapacityPanel = document.getElementById('audioCapacityPanel');
|
||||||
const stepCarrierTypeSummary = document.getElementById('stepCarrierTypeSummary');
|
|
||||||
|
|
||||||
carrierTypeRadios.forEach(radio => {
|
carrierTypeRadios.forEach(radio => {
|
||||||
radio.addEventListener('change', function() {
|
radio.addEventListener('change', function() {
|
||||||
@@ -586,23 +564,18 @@ carrierTypeRadios.forEach(radio => {
|
|||||||
if (capacityPanel) capacityPanel.classList.add('d-none');
|
if (capacityPanel) capacityPanel.classList.add('d-none');
|
||||||
if (audioCapacityPanel) audioCapacityPanel.classList.add('d-none');
|
if (audioCapacityPanel) audioCapacityPanel.classList.add('d-none');
|
||||||
|
|
||||||
// Update summary
|
// Select default mode for the active type and update hint
|
||||||
if (stepCarrierTypeSummary) {
|
|
||||||
stepCarrierTypeSummary.textContent = isAudio ? 'Audio' : 'Image';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Select default mode for the active type
|
|
||||||
if (isAudio) {
|
if (isAudio) {
|
||||||
const audioLsb = document.getElementById('modeAudioLsb');
|
const audioLsb = document.getElementById('modeAudioLsb');
|
||||||
if (audioLsb) audioLsb.checked = true;
|
if (audioLsb) { audioLsb.checked = true; audioLsb.dispatchEvent(new Event('change')); }
|
||||||
} else {
|
} else {
|
||||||
// Reset to DCT if available, else LSB
|
// Reset to DCT if available, else LSB
|
||||||
const dctRadio = document.getElementById('modeDct');
|
const dctRadio = document.getElementById('modeDct');
|
||||||
const lsbRadio = document.getElementById('modeLsb');
|
const lsbRadio = document.getElementById('modeLsb');
|
||||||
if (dctRadio && !dctRadio.disabled) {
|
if (dctRadio && !dctRadio.disabled) {
|
||||||
dctRadio.checked = true;
|
dctRadio.checked = true; dctRadio.dispatchEvent(new Event('change'));
|
||||||
} else if (lsbRadio) {
|
} 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.textContent = ref ? ref.name.slice(0, 15) : carrier.name.slice(0, 15);
|
||||||
summary.classList.remove('has-content');
|
summary.classList.remove('has-content');
|
||||||
stepNum.classList.remove('complete');
|
stepNum.classList.remove('complete');
|
||||||
stepNum.textContent = '2';
|
stepNum.textContent = '1';
|
||||||
} else {
|
} else {
|
||||||
summary.textContent = isAudio ? 'Select reference & audio' : 'Select reference & carrier';
|
summary.textContent = isAudio ? 'Select reference & audio' : 'Select reference & carrier';
|
||||||
summary.classList.remove('has-content');
|
summary.classList.remove('has-content');
|
||||||
stepNum.classList.remove('complete');
|
stepNum.classList.remove('complete');
|
||||||
stepNum.textContent = '2';
|
stepNum.textContent = '1';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -720,7 +693,7 @@ function updatePayloadSummary() {
|
|||||||
summary.textContent = 'Message or file to hide';
|
summary.textContent = 'Message or file to hide';
|
||||||
summary.classList.remove('has-content');
|
summary.classList.remove('has-content');
|
||||||
stepNum.classList.remove('complete');
|
stepNum.classList.remove('complete');
|
||||||
stepNum.textContent = '3';
|
stepNum.textContent = '2';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -748,7 +721,7 @@ function updateSecuritySummary() {
|
|||||||
summary.textContent = 'Passphrase & keys';
|
summary.textContent = 'Passphrase & keys';
|
||||||
summary.classList.remove('has-content');
|
summary.classList.remove('has-content');
|
||||||
stepNum.classList.remove('complete');
|
stepNum.classList.remove('complete');
|
||||||
stepNum.textContent = '4';
|
stepNum.textContent = '3';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user