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">
|
||||
|
||||
<!-- ================================================================
|
||||
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">
|
||||
<label class="form-label">
|
||||
<i class="bi bi-file-earmark me-1"></i> Carrier File
|
||||
</label>
|
||||
<div id="imageCarrierSection">
|
||||
<label class="form-label">
|
||||
<i class="bi bi-file-earmark-image me-1"></i> Carrier Image
|
||||
</label>
|
||||
<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,59 +240,74 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Embedding Mode (compact inline) -->
|
||||
<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">
|
||||
<input type="radio" class="btn-check" name="embed_mode" id="modeDct" value="dct" {% if has_dct %}checked{% endif %} {% if not has_dct %}disabled{% endif %}>
|
||||
<label class="btn btn-outline-secondary btn-sm text-nowrap" for="modeDct" id="dctModeLabel"><i class="bi bi-soundwave me-1"></i>DCT</label>
|
||||
<input type="radio" class="btn-check" name="embed_mode" id="modeLsb" value="lsb" {% if not has_dct %}checked{% endif %}>
|
||||
<label class="btn btn-outline-secondary btn-sm text-nowrap" for="modeLsb"><i class="bi bi-grid-3x3-gap me-1"></i>LSB</label>
|
||||
<!-- 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">
|
||||
<input type="radio" class="btn-check" name="embed_mode" id="modeDct" value="dct" {% if has_dct %}checked{% endif %} {% if not has_dct %}disabled{% endif %}>
|
||||
<label class="btn btn-outline-secondary btn-sm text-nowrap" for="modeDct" id="dctModeLabel"><i class="bi bi-soundwave me-1"></i>DCT</label>
|
||||
<input type="radio" class="btn-check" name="embed_mode" id="modeLsb" value="lsb" {% if not has_dct %}checked{% endif %}>
|
||||
<label class="btn btn-outline-secondary btn-sm text-nowrap" for="modeLsb"><i class="bi bi-grid-3x3-gap me-1"></i>LSB</label>
|
||||
</div>
|
||||
<span class="text-muted d-none d-sm-inline">|</span>
|
||||
<span class="d-flex gap-2 align-items-center" id="outputOptions">
|
||||
<div class="btn-group btn-group-sm" role="group">
|
||||
<input type="radio" class="btn-check" name="dct_color_mode" id="colorMode" value="color" checked>
|
||||
<label class="btn btn-outline-secondary btn-sm" for="colorMode">Color</label>
|
||||
<input type="radio" class="btn-check" name="dct_color_mode" id="grayMode" value="grayscale">
|
||||
<label class="btn btn-outline-secondary btn-sm" for="grayMode" id="grayModeLabel">Gray</label>
|
||||
</div>
|
||||
<div class="btn-group btn-group-sm" role="group">
|
||||
<input type="radio" class="btn-check" name="dct_output_format" id="jpegFormat" value="jpeg" checked>
|
||||
<label class="btn btn-outline-secondary btn-sm" for="jpegFormat" id="jpegFormatLabel">JPEG</label>
|
||||
<input type="radio" class="btn-check" name="dct_output_format" id="pngFormat" value="png">
|
||||
<label class="btn btn-outline-secondary btn-sm" for="pngFormat">PNG</label>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<span class="text-muted d-none d-sm-inline">|</span>
|
||||
<span class="d-flex gap-2 align-items-center" id="outputOptions">
|
||||
<div class="btn-group btn-group-sm" role="group">
|
||||
<input type="radio" class="btn-check" name="dct_color_mode" id="colorMode" value="color" checked>
|
||||
<label class="btn btn-outline-secondary btn-sm" for="colorMode">Color</label>
|
||||
<input type="radio" class="btn-check" name="dct_color_mode" id="grayMode" value="grayscale">
|
||||
<label class="btn btn-outline-secondary btn-sm" for="grayMode" id="grayModeLabel">Gray</label>
|
||||
<!-- Audio Modes (hidden by default) -->
|
||||
<div class="d-none" id="audioModeGroup">
|
||||
<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="dct_output_format" id="jpegFormat" value="jpeg" checked>
|
||||
<label class="btn btn-outline-secondary btn-sm" for="jpegFormat" id="jpegFormatLabel">JPEG</label>
|
||||
<input type="radio" class="btn-check" name="dct_output_format" id="pngFormat" value="png">
|
||||
<label class="btn btn-outline-secondary btn-sm" for="pngFormat">PNG</label>
|
||||
<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>
|
||||
</span>
|
||||
{% 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>
|
||||
|
||||
<!-- Audio Modes (hidden by default) -->
|
||||
<div class="d-none" id="audioModeGroup">
|
||||
<div class="btn-group btn-group-sm" 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>
|
||||
</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';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user