Add full emoji library with improved display and prominent invisible text button

This commit is contained in:
EP
2025-03-09 19:38:18 -04:00
parent 50b74e6628
commit 633b93c092
4 changed files with 745 additions and 20 deletions
+381 -1
View File
@@ -179,6 +179,214 @@ textarea {
transition: all 0.2s ease;
}
/* Special styling for encoded message textarea */
.output-section textarea {
background-color: var(--secondary-bg);
color: var(--accent-color);
border: 1px solid rgba(100, 181, 246, 0.3);
border-radius: 6px;
font-family: 'Fira Code', monospace;
letter-spacing: 0.5px;
line-height: 1.5;
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.2);
padding: 14px;
font-weight: 500;
}
/* Emoji Library Styling */
.emoji-library {
margin: 16px 0;
background-color: var(--main-bg-color);
border-radius: 8px;
padding: 16px;
border: 1px solid var(--input-border);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.emoji-library-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
border-bottom: 1px solid var(--input-border);
padding-bottom: 12px;
}
.emoji-library-title {
font-size: 1.1rem;
font-weight: 600;
display: flex;
flex-direction: column;
gap: 4px;
}
.emoji-library-title i {
margin-right: 8px;
color: var(--accent-color);
}
.emoji-library-subtitle {
font-size: 0.8rem;
font-weight: normal;
opacity: 0.7;
margin-top: 4px;
}
.emoji-search {
position: relative;
width: 220px;
}
.emoji-search input {
width: 100%;
padding: 8px 12px 8px 32px;
background-color: var(--input-bg);
border: 1px solid var(--input-border);
border-radius: 4px;
color: var(--text-color);
font-size: 0.9rem;
transition: all 0.2s ease;
}
.emoji-search input:focus {
border-color: var(--accent-color);
box-shadow: 0 0 0 2px rgba(var(--accent-color-rgb), 0.2);
outline: none;
}
.emoji-search i {
position: absolute;
left: 10px;
top: 50%;
transform: translateY(-50%);
color: var(--text-color);
opacity: 0.5;
}
.emoji-grid-wrapper {
border-radius: 6px;
background-color: var(--secondary-bg);
padding: 8px;
margin-bottom: 12px;
}
.emoji-grid-note {
background-color: rgba(var(--accent-color-rgb), 0.1);
padding: 8px 12px;
border-radius: 4px;
margin-bottom: 8px;
font-size: 0.9rem;
color: var(--text-color);
display: flex;
align-items: center;
gap: 8px;
}
.emoji-grid-note i {
color: var(--accent-color);
}
.emoji-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(42px, 1fr));
gap: 10px;
max-height: 250px;
overflow-y: auto;
padding: 8px;
scrollbar-width: thin;
border-radius: 6px;
border: 1px solid var(--input-border);
}
.emoji-button {
display: flex;
align-items: center;
justify-content: center;
width: 38px;
height: 38px;
font-size: 1.25rem;
background-color: var(--button-bg);
border: 1px solid transparent;
border-radius: 6px;
cursor: pointer;
transition: all 0.2s ease;
padding: 0;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
.emoji-button:hover {
background-color: var(--button-hover-bg);
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
border-color: var(--accent-color);
}
.emoji-button:active {
transform: translateY(0);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
/* Emoji Library Footer */
.emoji-library-footer {
display: flex;
justify-content: flex-start;
align-items: center;
margin-top: 12px;
padding-top: 12px;
border-top: 1px solid var(--input-border);
}
.selected-emoji-info {
display: flex;
align-items: center;
gap: 8px;
}
.selected-emoji {
font-size: 1.5rem;
padding: 4px 8px;
background-color: var(--secondary-bg);
border-radius: 6px;
border: 1px solid var(--input-border);
}
.selected-emoji-label {
font-size: 0.85rem;
color: var(--text-muted);
font-style: italic;
}
.emoji-button.selected {
background-color: var(--accent-color);
color: var(--main-bg-color);
border-color: var(--accent-color);
}
.emoji-category-tabs {
display: flex;
gap: 8px;
margin-bottom: 12px;
overflow-x: auto;
padding-bottom: 4px;
}
.emoji-category-tab {
padding: 6px 12px;
background-color: var(--button-bg);
border: 1px solid var(--input-border);
border-radius: 4px;
color: var(--text-color);
font-size: 0.8rem;
cursor: pointer;
white-space: nowrap;
}
.emoji-category-tab.active {
background-color: var(--accent-color);
color: var(--main-bg-color);
border-color: var(--accent-color);
}
textarea::placeholder {
color: rgba(224, 224, 224, 0.5);
}
@@ -199,6 +407,43 @@ button:active {
transform: translateY(1px);
}
/* Invisible Text Button */
.invisible-button {
width: 100%;
margin-top: 12px;
padding: 12px;
background-color: var(--secondary-bg);
border: 2px dashed var(--accent-color);
transition: all 0.3s ease;
display: flex;
justify-content: center;
position: relative;
}
.invisible-button .carrier-content {
display: flex;
align-items: center;
justify-content: center;
font-size: 1.1rem;
}
.invisible-button .carrier-emoji {
font-size: 1.5rem;
margin-right: 12px;
}
.invisible-button:hover {
background-color: rgba(var(--accent-color-rgb), 0.1);
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.invisible-button.active {
background-color: var(--accent-color);
color: var(--main-bg-color);
border-style: solid;
}
button {
background-color: var(--button-bg);
color: var(--text-color);
@@ -261,6 +506,52 @@ button:hover {
gap: 12px;
}
/* Carrier styling */
.carrier-content {
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
margin-bottom: 6px;
}
.carrier-emoji {
font-size: 1.5rem;
line-height: 1;
display: block;
margin-bottom: 2px;
}
.carrier-name {
font-size: 0.85rem;
font-weight: 500;
}
/* Preview styling */
.transform-preview {
margin-top: auto;
width: 100%;
font-size: 0.7rem;
padding: 4px;
background-color: rgba(0, 0, 0, 0.1);
border-radius: 3px;
white-space: normal;
line-height: 1.2;
}
.preview-label {
color: var(--accent-color);
opacity: 0.8;
font-weight: 500;
display: block;
margin-bottom: 2px;
}
.preview-ellipsis {
color: var(--accent-color);
font-weight: bold;
}
/* Encoded preview styling */
.encoded-preview {
display: inline-block;
@@ -341,21 +632,110 @@ button:hover {
background: var(--button-bg);
color: var(--text-color);
transition: all 0.2s ease;
overflow: hidden;
}
.transform-button:before {
content: '';
position: absolute;
top: -2px;
left: -2px;
right: -2px;
bottom: -2px;
border: 2px solid transparent;
border-radius: 6px;
transition: all 0.2s ease;
pointer-events: none;
}
.transform-button:hover {
background: var(--button-hover-bg);
color: var(--main-bg-color);
border-color: var(--accent-color);
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
.transform-button:hover:before {
border-color: var(--accent-color);
opacity: 0.4;
}
.transform-button.active {
background: var(--button-active-bg);
color: var(--main-bg-color);
border-color: var(--button-active-bg);
box-shadow: 0 2px 12px rgba(100, 181, 246, 0.3);
}
.transform-button.active:before {
border-color: var(--accent-color);
opacity: 1;
}
/* Add a subtle indicator for clickable buttons */
.transform-button:after {
content: '';
position: absolute;
bottom: 4px;
left: 50%;
transform: translateX(-50%);
width: 20px;
height: 2px;
background: var(--accent-color);
opacity: 0;
transition: all 0.2s ease;
}
.transform-button.active:after {
opacity: 0.8;
width: 30px;
}
/* Output section */
.output-section {
display: flex;
flex-direction: column;
gap: 10px;
margin-top: 16px;
padding: 16px;
background: var(--main-bg-color);
border-radius: 6px;
border: 1px solid var(--accent-color);
box-shadow: 0 2px 12px rgba(100, 181, 246, 0.15);
}
.output-heading h4 {
display: flex;
align-items: center;
gap: 8px;
color: var(--success-color);
margin-bottom: 8px;
font-size: 1rem;
}
.output-heading h4 small {
color: var(--accent-color);
font-size: 0.8rem;
margin-left: 8px;
font-weight: normal;
opacity: 0.8;
}
.output-instructions {
font-size: 0.85rem;
color: var(--text-color);
opacity: 0.7;
padding: 6px;
border-radius: 4px;
background-color: rgba(0, 0, 0, 0.2);
margin-top: 8px;
}
.output-instructions i {
color: var(--accent-color);
margin-right: 6px;
}
.output-container {
position: relative;
}
+57 -12
View File
@@ -79,32 +79,76 @@
@click="selectCarrier(carrier)"
:title="carrier.desc"
>
{{ carrier.emoji }} {{ carrier.name }}
<div class="carrier-content">
<span class="carrier-emoji">{{ carrier.emoji }}</span>
<span class="carrier-name">{{ carrier.name }}</span>
</div>
<small class="transform-preview" v-if="emojiMessage">
<span class="preview-label">Preview:</span>
<span class="encoded-preview">{{ carrier.preview ? carrier.preview(emojiMessage.slice(0, 10)) : '' }}</span>
<span class="preview-ellipsis" v-if="emojiMessage.length > 10">...</span>
</small>
</button>
<button
class="transform-button"
:class="{ active: activeSteg === 'invisible' }"
@click="setStegMode('invisible')"
title="Make text invisible using zero-width characters"
>
<i class="fas fa-eye-slash"></i> Invisible
<small class="transform-preview" v-if="emojiMessage">
{{ previewInvisible(emojiMessage.slice(0, 10)) }}
</small>
</button>
</div>
<!-- Big Invisible Text Button -->
<button
class="transform-button invisible-button"
:class="{ active: activeSteg === 'invisible' }"
@click="setStegMode('invisible')"
title="Make text invisible using zero-width characters"
>
<div class="carrier-content">
<span class="carrier-emoji"><i class="fas fa-eye-slash"></i></span>
<span class="carrier-name">Invisible Text Mode</span>
</div>
<small class="transform-preview" v-if="emojiMessage">
<span class="preview-label">Preview:</span>
<span class="encoded-preview">{{ previewInvisible(emojiMessage.slice(0, 10)) }}</span>
<span class="preview-ellipsis" v-if="emojiMessage.length > 10">...</span>
</small>
</button>
</div>
<!-- Emoji Library Section -->
<div class="emoji-library">
<div class="emoji-library-header">
<div class="emoji-library-title">
<i class="fas fa-icons"></i> Quick Emoji Picker
<span class="emoji-library-subtitle">Click any emoji to insert it at cursor position</span>
</div>
<div class="emoji-search">
<i class="fas fa-search"></i>
<input type="text" placeholder="Search emojis..." v-model="emojiSearch" @input="filterEmojis">
</div>
</div>
<div id="emoji-grid-container" class="emoji-grid-wrapper"></div>
<div class="emoji-library-footer" v-if="selectedEmoji">
<div class="selected-emoji-info">
<span class="selected-emoji">{{ selectedEmoji }}</span>
<span class="selected-emoji-label">Last selected</span>
</div>
</div>
</div>
<div class="output-section" v-if="encodedMessage">
<div class="output-heading">
<h4>
<i class="fas fa-check-circle"></i>
Encoded Message
<small v-if="selectedCarrier">using {{ selectedCarrier.name }}</small>
<small v-else-if="activeSteg === 'invisible'">using Invisible Text</small>
</h4>
</div>
<div class="output-container">
<textarea readonly v-model="encodedMessage"></textarea>
<button class="copy-button" @click="copyToClipboard(encodedMessage)" title="Copy to clipboard">
<i class="fas fa-copy"></i>
</button>
</div>
<div class="output-instructions">
<small><i class="fas fa-info-circle"></i> Copy this text and share it. Only people who know how to decode it will be able to read your message.</small>
</div>
</div>
<div class="decode-section" v-if="showDecoder">
@@ -179,6 +223,7 @@
<script src="js/transforms.js"></script>
<script src="js/steganography.js"></script>
<script src="js/emojiLibrary.js"></script>
<script src="js/app.js"></script>
</body>
</html>
+81 -7
View File
@@ -26,7 +26,11 @@ new Vue({
selectedCarrier: null,
activeSteg: null,
carriers: window.steganography.carriers,
showDecoder: true
showDecoder: true,
// Emoji Library
emojiSearch: '',
filteredEmojis: [...window.emojiLibrary.EMOJI_LIST],
selectedEmoji: null
},
methods: {
// Theme Toggle
@@ -52,13 +56,29 @@ new Vue({
// Steganography Methods
selectCarrier(carrier) {
this.selectedCarrier = carrier;
this.activeSteg = 'emoji';
this.autoEncode();
// Toggle carrier selection if clicking the same one again
if (this.selectedCarrier === carrier) {
this.selectedCarrier = null;
this.encodedMessage = '';
} else {
this.selectedCarrier = carrier;
this.activeSteg = 'emoji';
this.autoEncode();
}
},
setStegMode(mode) {
this.activeSteg = mode;
this.autoEncode();
// Toggle mode selection if clicking the same one again
if (this.activeSteg === mode) {
this.activeSteg = null;
this.encodedMessage = '';
} else {
this.activeSteg = mode;
// When switching to invisible mode, clear the carrier selection
if (mode === 'invisible') {
this.selectedCarrier = null;
}
this.autoEncode();
}
},
autoEncode() {
if (!this.emojiMessage) {
@@ -249,13 +269,67 @@ new Vue({
}
return null;
},
// Emoji Library Methods
filterEmojis() {
if (!this.emojiSearch) {
this.filteredEmojis = [...window.emojiLibrary.EMOJI_LIST];
this.renderEmojiGrid();
return;
}
const searchTerm = this.emojiSearch.toLowerCase();
this.filteredEmojis = window.emojiLibrary.EMOJI_LIST.filter(emoji => {
// Simple search - we could enhance this with emoji names/descriptions later
return emoji.toLowerCase().includes(searchTerm);
});
this.renderEmojiGrid();
},
selectEmoji(emoji) {
this.selectedEmoji = emoji;
// Insert the emoji at cursor position in the textarea
const textarea = document.getElementById('steg-input');
if (textarea) {
const startPos = textarea.selectionStart;
const endPos = textarea.selectionEnd;
const currentValue = this.emojiMessage;
// Insert emoji at cursor position
this.emojiMessage = currentValue.substring(0, startPos) + emoji + currentValue.substring(endPos);
// Set cursor position after the inserted emoji
this.$nextTick(() => {
textarea.focus();
textarea.selectionStart = startPos + emoji.length;
textarea.selectionEnd = startPos + emoji.length;
// Trigger encoding
this.autoEncode();
});
this.showNotification(`Emoji ${emoji} inserted`);
}
},
renderEmojiGrid() {
window.emojiLibrary.renderEmojiGrid('emoji-grid-container', this.selectEmoji.bind(this), this.filteredEmojis);
}
},
// Initialize theme
// Initialize theme and components
mounted() {
// Apply theme
if (this.isDarkTheme) {
document.body.classList.add('dark-theme');
}
// Initialize emoji grid
this.$nextTick(() => {
this.renderEmojiGrid();
});
},
// Keyboard shortcuts
created() {
+226
View File
@@ -0,0 +1,226 @@
// Emoji Library for P4RS3LT0NGV3
// Create namespace for emoji library
window.emojiLibrary = {};
// Make emoji list globally available
window.emojiLibrary.EMOJI_LIST = [
// Faces and People
"😀", // Grinning Face
"😂", // Face with Tears of Joy
"🥰", // Smiling Face with Hearts
"😎", // Smiling Face with Sunglasses
"🤔", // Thinking Face
"😅", // Smiling Face with Sweat
"😊", // Smiling Face with Smiling Eyes
"😇", // Smiling Face with Halo
"🙃", // Upside-Down Face
"😉", // Winking Face
"🥳", // Partying Face
"😴", // Sleeping Face
"🥱", // Yawning Face
"🤯", // Exploding Head
"🧠", // Brain
// Gestures and Body Parts
"👍", // Thumbs Up
"👎", // Thumbs Down
"👏", // Clapping Hands
"🤝", // Handshake
"👋", // Waving Hand
"✌️", // Victory Hand
"🤟", // Love-You Gesture
"👀", // Eyes
"👁️", // Eye
"🧿", // Nazar Amulet
// Celebration & Objects
"🎉", // Party Popper
"🎂", // Birthday Cake
"🎁", // Wrapped Gift
"🏆", // Trophy
"🏅", // Sports Medal
"💰", // Money Bag
"💸", // Money with Wings
"💯", // Hundred Points
"📱", // Mobile Phone
"💻", // Laptop
"⌨️", // Keyboard
"🔒", // Locked
"🔓", // Unlocked
// Food & Drink
"🍕", // Pizza
"🍔", // Hamburger
"🍦", // Ice Cream
"🍩", // Doughnut
"🍺", // Beer Mug
"🍷", // Wine Glass
"☕", // Hot Beverage
// Nature & Weather
"🌈", // Rainbow
"🌞", // Sun with Face
"🌙", // Crescent Moon
"⭐", // Star
"🌟", // Glowing Star
"⚡", // High Voltage
"❄️", // Snowflake
"🔥", // Fire
"💧", // Droplet
"🌊", // Water Wave
// Animals
"🐱", // Cat Face
"🐶", // Dog Face
"🦊", // Fox
"🐼", // Panda
"🦁", // Lion
"🐬", // Dolphin
"🦄", // Unicorn
// Symbols & Special
"❤️", // Red Heart
"🧡", // Orange Heart
"💚", // Green Heart
"💙", // Blue Heart
"💜", // Purple Heart
"🚀", // Rocket
"👀", // Eyes
"💀", // Skull
"🥹", // Face Holding Back Tears
"🐍", // Snake
"🐉", // Dragon
"🐲", // Dragon Face
"🧙‍♂️", // Wizard
"🪄", // Magic Wand
"🏴‍☠️", // Pirate Flag
"🦅", // Eagle (often associated with pirates)
"🦜", // Parrot (pirate symbol)
"💻", // Laptop (hacker symbol)
"🕶️", // Sunglasses (cool guy symbol)
"🧑‍💻", // Technologist
"👨‍💻", // Man Technologist
"👩‍💻", // Woman Technologist
"🕵️", // Detective
"🕵️‍♂️", // Man Detective
"🕵️‍♀️", // Woman Detective
"🖥️", // Desktop Computer
"⌨️", // Keyboard
"🖱️", // Computer Mouse
"🕹️", // Joystick
"📱", // Mobile Phone
"📲", // Mobile Phone with Arrow
"🔓", // Unlocked (hacker symbol)
"🔑", // Key (hacker symbol)
"🗝️", // Old Key (hacker symbol)
"🛡️", // Shield (hacker symbol)
"⚔️", // Crossed Swords (hacker symbol)
"🧬", // DNA (hacker symbol)
"🧫", // Petri Dish (hacker symbol)
"🧪", // Test Tube (hacker symbol)
"🛠️", // Hammer and Wrench (hacker symbol)
"⚙️", // Gear (hacker symbol)
"🧰", // Toolbox (hacker symbol)
"🧲", // Magnet (hacker symbol)
"💣", // Bomb (hacker symbol)
"🕳️", // Hole (hacker symbol)
"📡", // Satellite Antenna (hacker symbol)
"🛰️", // Satellite (hacker symbol)
"📞", // Telephone Receiver (hacker symbol)
"☎️", // Telephone (hacker symbol)
"📟", // Pager (hacker symbol)
"📠", // Fax Machine (hacker symbol)
"🔌", // Electric Plug (hacker symbol)
"💡", // Light Bulb (hacker symbol)
"🔦", // Flashlight (hacker symbol)
"🕯️", // Candle (hacker symbol)
"🗞️", // Rolled-Up Newspaper (hacker symbol)
"📜", // Scroll (hacker symbol)
"📃", // Page with Curl (hacker symbol)
"📄", // Page Facing Up (hacker symbol)
"📑", // Bookmark Tabs (hacker symbol)
"📊", // Bar Chart (hacker symbol)
"📈", // Chart Increasing (hacker symbol)
"📉", // Chart Decreasing (hacker symbol)
"🗂️", // Card Index Dividers (hacker symbol)
"🗃️", // Card File Box (hacker symbol)
"🗄️", // File Cabinet (hacker symbol)
"🗑️", // Wastebasket (hacker symbol)
"🛢️", // Oil Drum (hacker symbol)
"🛎️", // Bellhop Bell (hacker symbol)
"🧳", // Luggage (hacker symbol)
"🛌", // Person in Bed (hacker symbol)
"🛏️", // Bed (hacker symbol)
"🛋️", // Couch and Lamp (hacker symbol)
"🪑", // Chair (hacker symbol)
"🚪", // Door (hacker symbol)
"🧴", // Lotion Bottle (hacker symbol)
"🧷", // Safety Pin (hacker symbol)
"🧹", // Broom (hacker symbol)
"🧺", // Basket (hacker symbol)
"🧻", // Roll of Paper (hacker symbol)
"🧼", // Soap (hacker symbol)
"🧽", // Sponge (hacker symbol)
"🧯", // Fire Extinguisher (hacker symbol)
"🛒", // Shopping Cart (hacker symbol)
"🚬", // Cigarette (hacker symbol)
"⚰️", // Coffin (hacker symbol)
"⚱️", // Funeral Urn (hacker symbol)
"🗿", // Moai (hacker symbol)
"🛂", // Passport Control (hacker symbol)
"🛃", // Customs (hacker symbol)
"🛄", // Baggage Claim (hacker symbol)
"🛅", // Left Luggage (hacker symbol)
"🚹", // Men's Room (hacker symbol)
"🚺", // Women's Room (hacker symbol)
"🚼", // Baby Symbol (hacker symbol)
"🚻", // Restroom (hacker symbol)
"🚮", // Litter in Bin Sign (hacker symbol)
"🚰", // Potable Water (hacker symbol)
"🚾", // Water Closet (hacker symbol)
"🚭", // No Smoking (hacker symbol)
"🚯", // No Littering (hacker symbol)
"🚱", // Non-Potable Water (hacker symbol)
];
// Function to render emoji grid
window.emojiLibrary.renderEmojiGrid = function(containerId, onEmojiSelect, filteredList) {
const container = document.getElementById(containerId);
if (!container) return;
// Clear container
container.innerHTML = '';
// Create emoji grid
const gridContainer = document.createElement('div');
gridContainer.className = 'emoji-grid';
// Set a heading explaining the full library is shown
if (!filteredList || filteredList.length === window.emojiLibrary.EMOJI_LIST.length) {
const fullLibraryNote = document.createElement('div');
fullLibraryNote.className = 'emoji-grid-note';
fullLibraryNote.innerHTML = '<i class="fas fa-info-circle"></i> Showing all emojis. Use search to filter.';
container.appendChild(fullLibraryNote);
}
// Determine which list to use (filtered or full)
const emojisToShow = filteredList || window.emojiLibrary.EMOJI_LIST;
// Add emojis to grid
emojisToShow.forEach(emoji => {
const emojiButton = document.createElement('button');
emojiButton.className = 'emoji-button';
emojiButton.innerHTML = emoji;
emojiButton.title = 'Click to select this emoji';
emojiButton.addEventListener('click', () => {
if (typeof onEmojiSelect === 'function') {
onEmojiSelect(emoji);
}
});
gridContainer.appendChild(emojiButton);
});
container.appendChild(gridContainer);
};