Minor touches.
This commit is contained in:
@@ -15,10 +15,18 @@
|
|||||||
<h6><i class="bi bi-check-circle me-2"></i>Message Decrypted Successfully!</h6>
|
<h6><i class="bi bi-check-circle me-2"></i>Message Decrypted Successfully!</h6>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-4">
|
<label class="form-label text-muted">Decoded Message:</label>
|
||||||
|
<div class="position-relative">
|
||||||
|
<div class="alert-message p-3 rounded bg-dark border border-secondary" id="decodedContent" style="white-space: pre-wrap;">{{ decoded_message }}</div>
|
||||||
|
<button class="btn btn-sm btn-outline-light position-absolute top-0 end-0 m-2" onclick="navigator.clipboard.writeText(document.getElementById('decodedContent').innerText).then(() => this.innerHTML = '<i class=\'bi bi-check\'></i>').catch(() => alert('Failed to copy'))">
|
||||||
|
<i class="bi bi-clipboard"></i> Copy
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
i<!--then<div class="mb-4">
|
||||||
<label class="form-label text-muted">Decoded Message:</label>
|
<label class="form-label text-muted">Decoded Message:</label>
|
||||||
<div class="alert-message">{{ decoded_message }}</div>
|
<div class="alert-message">{{ decoded_message }}</div>
|
||||||
</div>
|
</div>-->
|
||||||
|
|
||||||
<a href="/decode" class="btn btn-outline-light w-100">
|
<a href="/decode" class="btn btn-outline-light w-100">
|
||||||
<i class="bi bi-arrow-repeat me-2"></i>Decode Another Message
|
<i class="bi bi-arrow-repeat me-2"></i>Decode Another Message
|
||||||
@@ -83,14 +91,18 @@
|
|||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6 mb-3">
|
<div class="col-md-6 mb-3">
|
||||||
<label class="form-label">
|
|
||||||
<i class="bi bi-123 me-1"></i> PIN
|
<label class="form-label"><i class="bi bi-123 me-1"></i> PIN</label>
|
||||||
</label>
|
<div class="input-group">
|
||||||
<input type="password" name="pin" class="form-control" id="pinInput"
|
<input type="password" name="pin" class="form-control" id="pinInput" placeholder="6-9 digits" maxlength="9">
|
||||||
placeholder="6-9 digits" maxlength="9">
|
<button class="btn btn-outline-secondary" type="button" id="togglePin">
|
||||||
|
<i class="bi bi-eye"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<div class="form-text">
|
<div class="form-text">
|
||||||
If PIN was used during encoding
|
If PIN was used during encoding
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-6 mb-3">
|
<div class="col-md-6 mb-3">
|
||||||
@@ -197,6 +209,45 @@ function updateDayLabel(dayName) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 1. PIN Toggle
|
||||||
|
document.getElementById('togglePin')?.addEventListener('click', function() {
|
||||||
|
const input = document.getElementById('pinInput');
|
||||||
|
const icon = this.querySelector('i');
|
||||||
|
if (input.type === 'password') {
|
||||||
|
input.type = 'text';
|
||||||
|
icon.classList.replace('bi-eye', 'bi-eye-slash');
|
||||||
|
} else {
|
||||||
|
input.type = 'password';
|
||||||
|
icon.classList.replace('bi-eye-slash', 'bi-eye');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2. Paste from Clipboard
|
||||||
|
document.addEventListener('paste', function(e) {
|
||||||
|
// Only run if the form exists (we are not on the success page)
|
||||||
|
if (!document.getElementById('decodeForm')) return;
|
||||||
|
|
||||||
|
const items = e.clipboardData.items;
|
||||||
|
for (let i = 0; i < items.length; i++) {
|
||||||
|
if (items[i].type.indexOf("image") !== -1) {
|
||||||
|
const blob = items[i].getAsFile();
|
||||||
|
|
||||||
|
// Priority for Decode: Fill Stego Image first (most common), then Reference
|
||||||
|
const stegoInput = document.querySelector('input[name="stego_image"]');
|
||||||
|
const refInput = document.querySelector('input[name="reference_photo"]');
|
||||||
|
|
||||||
|
const targetInput = (!stegoInput.files.length) ? stegoInput : refInput;
|
||||||
|
|
||||||
|
const container = new DataTransfer();
|
||||||
|
container.items.add(blob);
|
||||||
|
targetInput.files = container.files;
|
||||||
|
|
||||||
|
targetInput.dispatchEvent(new Event('change'));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Drag & drop with preview
|
// Drag & drop with preview
|
||||||
document.querySelectorAll('.drop-zone').forEach(zone => {
|
document.querySelectorAll('.drop-zone').forEach(zone => {
|
||||||
const input = zone.querySelector('input[type="file"]');
|
const input = zone.querySelector('input[type="file"]');
|
||||||
|
|||||||
@@ -86,14 +86,14 @@
|
|||||||
|
|
||||||
<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"><i class="bi bi-123 me-1"></i> PIN</label>
|
||||||
<i class="bi bi-123 me-1"></i> PIN
|
<div class="input-group">
|
||||||
</label>
|
<input type="password" name="pin" class="form-control" id="pinInput" placeholder="6-9 digits" maxlength="9">
|
||||||
<input type="password" name="pin" class="form-control" id="pinInput"
|
<button class="btn btn-outline-secondary" type="button" id="togglePin">
|
||||||
placeholder="6-9 digits" maxlength="9">
|
<i class="bi bi-eye"></i>
|
||||||
<div class="form-text">
|
</button>
|
||||||
Your static 6-9 digit PIN (if configured)
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-text">Your static 6-9 digit PIN (if configured)</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-6 mb-3">
|
<div class="col-md-6 mb-3">
|
||||||
@@ -255,5 +255,61 @@ document.querySelectorAll('.drop-zone').forEach(zone => {
|
|||||||
reader.readAsDataURL(file);
|
reader.readAsDataURL(file);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
// 1. PIN Toggle Logic
|
||||||
|
document.getElementById('togglePin').addEventListener('click', function() {
|
||||||
|
const input = document.getElementById('pinInput');
|
||||||
|
const icon = this.querySelector('i');
|
||||||
|
if (input.type === 'password') {
|
||||||
|
input.type = 'text';
|
||||||
|
icon.classList.replace('bi-eye', 'bi-eye-slash');
|
||||||
|
} else {
|
||||||
|
input.type = 'password';
|
||||||
|
icon.classList.replace('bi-eye-slash', 'bi-eye');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2. Prevent Same File Selection
|
||||||
|
function checkDuplicateFiles() {
|
||||||
|
const refInput = document.querySelector('input[name="reference_photo"]');
|
||||||
|
const carInput = document.querySelector('input[name="carrier"]');
|
||||||
|
|
||||||
|
if (refInput.files[0] && carInput.files[0]) {
|
||||||
|
// Compare name and size as a proxy for identical files
|
||||||
|
if (refInput.files[0].name === carInput.files[0].name &&
|
||||||
|
refInput.files[0].size === carInput.files[0].size) {
|
||||||
|
alert("Security Warning: You cannot use the same image for both Reference and Carrier!");
|
||||||
|
carInput.value = ''; // Clear carrier
|
||||||
|
document.getElementById('carrierPreview').classList.add('d-none');
|
||||||
|
document.querySelector('#carrierDropZone .drop-zone-label').innerHTML =
|
||||||
|
'<i class="bi bi-cloud-arrow-up fs-3 d-block mb-2 text-muted"></i>' +
|
||||||
|
'<span class="text-muted">Drop image or click to browse</span>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.querySelector('input[name="reference_photo"]').addEventListener('change', checkDuplicateFiles);
|
||||||
|
document.querySelector('input[name="carrier"]').addEventListener('change', checkDuplicateFiles);
|
||||||
|
|
||||||
|
// 3. Paste from Clipboard
|
||||||
|
document.addEventListener('paste', function(e) {
|
||||||
|
const items = e.clipboardData.items;
|
||||||
|
for (let i = 0; i < items.length; i++) {
|
||||||
|
if (items[i].type.indexOf("image") !== -1) {
|
||||||
|
const blob = items[i].getAsFile();
|
||||||
|
|
||||||
|
// Priority: Fill Carrier first, if empty. Else fill Reference.
|
||||||
|
const carrierInput = document.querySelector('input[name="carrier"]');
|
||||||
|
const refInput = document.querySelector('input[name="reference_photo"]');
|
||||||
|
|
||||||
|
const targetInput = (!carrierInput.files.length) ? carrierInput : refInput;
|
||||||
|
|
||||||
|
const container = new DataTransfer();
|
||||||
|
container.items.add(blob);
|
||||||
|
targetInput.files = container.files;
|
||||||
|
|
||||||
|
targetInput.dispatchEvent(new Event('change'));
|
||||||
|
break; // Only paste one image at a time
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -3,6 +3,18 @@
|
|||||||
{% block title %}Generate Credentials - Stegasoo{% endblock %}
|
{% block title %}Generate Credentials - Stegasoo{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
<style>
|
||||||
|
@media print {
|
||||||
|
body * { visibility: hidden; }
|
||||||
|
.card-body, .card-body * { visibility: visible; color: black !important; }
|
||||||
|
.card-body { position: absolute; left: 0; top: 0; width: 100%; margin: 0; border: none; }
|
||||||
|
.btn, .alert, form, .card-header { display: none !important; }
|
||||||
|
.table { border-collapse: collapse; width: 100%; }
|
||||||
|
th, td { border: 1px solid #000 !important; padding: 8px; }
|
||||||
|
/* Hide the inputs, show the results */
|
||||||
|
#generateForm { display: none; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-lg-8">
|
<div class="col-lg-8">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
@@ -120,16 +132,16 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if pin %}
|
{% if pin %}
|
||||||
<hr class="my-4">
|
|
||||||
<div class="text-center mb-4">
|
<div class="text-center mb-4">
|
||||||
<h6 class="text-muted mb-2">YOUR STATIC PIN</h6>
|
<h6 class="text-muted mb-2">YOUR STATIC PIN</h6>
|
||||||
<div class="pin-container">
|
<div class="pin-container p-3 bg-dark border rounded fs-1 font-monospace text-white">
|
||||||
<div class="pin-display">{{ pin }}</div>
|
{{ pin }}
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-2">
|
<div class="mt-2 d-print-none"> <small class="text-muted">Use this {{ pin_length }}-digit PIN every day</small>
|
||||||
<small class="text-muted">Use this {{ pin_length }}-digit PIN every day</small>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if rsa_key_pem %}
|
{% if rsa_key_pem %}
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ def get_data_dir() -> Path:
|
|||||||
# Check multiple locations
|
# Check multiple locations
|
||||||
candidates = [
|
candidates = [
|
||||||
Path(__file__).parent.parent.parent.parent / 'data', # Development
|
Path(__file__).parent.parent.parent.parent / 'data', # Development
|
||||||
|
Path(__file__).parent.parent / 'data', # Development
|
||||||
Path(__file__).parent / 'data', # Installed package
|
Path(__file__).parent / 'data', # Installed package
|
||||||
Path('/app/data'), # Docker
|
Path('/app/data'), # Docker
|
||||||
Path.cwd() / 'data', # Current directory
|
Path.cwd() / 'data', # Current directory
|
||||||
|
|||||||
BIN
test_data/c643e6b9_20251228.png
Normal file
BIN
test_data/c643e6b9_20251228.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 MiB |
Reference in New Issue
Block a user