diff --git a/css/style.css b/css/style.css new file mode 100644 index 0000000..29d6aca --- /dev/null +++ b/css/style.css @@ -0,0 +1,228 @@ +/* Dark hacker theme styling */ +:root { + --main-bg-color: #0E1117; + --text-color: #00FF41; + --button-bg: #1E1E1E; + --button-border: #00FF41; + --button-hover-bg: #00FF41; + --button-hover-text: #1E1E1E; + --input-bg: #1E1E1E; + --input-border: #00FF41; + --section-divider: #00FF41; +} + +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + background-color: var(--main-bg-color); + color: var(--text-color); + font-family: 'Courier New', monospace; + line-height: 1.6; + padding: 20px; +} + +.container { + max-width: 1200px; + margin: 0 auto; + padding: 20px; +} + +header { + text-align: center; + margin-bottom: 30px; +} + +h1, h2, h3, h4, h5 { + font-family: 'Courier New', monospace; + color: var(--text-color); + margin-bottom: 15px; +} + +/* Tabs styling */ +.tabs { + margin-top: 20px; +} + +.tab-buttons { + display: flex; + margin-bottom: 20px; + border-bottom: 1px solid var(--text-color); +} + +.tab-buttons button { + background-color: transparent; + color: var(--text-color); + border: none; + padding: 10px 20px; + cursor: pointer; + font-size: 16px; + font-family: 'Courier New', monospace; + transition: all 0.3s; +} + +.tab-buttons button.active { + border-bottom: 3px solid var(--text-color); + font-weight: bold; +} + +.tab-buttons button:hover { + background-color: rgba(0, 255, 65, 0.1); +} + +.tab-content { + padding: 20px 0; +} + +/* Form elements */ +.form-group { + margin-bottom: 20px; +} + +label { + display: block; + margin-bottom: 8px; + font-weight: bold; +} + +textarea { + width: 100%; + padding: 12px; + background-color: var(--input-bg); + color: var(--text-color); + border: 1px solid var(--input-border); + border-radius: 4px; + font-family: 'Courier New', monospace; + resize: vertical; + min-height: 100px; +} + +button { + background-color: var(--button-bg); + color: var(--text-color); + border: 1px solid var(--button-border); + border-radius: 4px; + padding: 8px 16px; + cursor: pointer; + font-family: 'Courier New', monospace; + transition: all 0.3s; +} + +button:hover { + background-color: var(--button-hover-bg); + color: var(--button-hover-text); +} + +/* Emoji grid */ +.emoji-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(60px, 1fr)); + gap: 10px; + margin-top: 10px; +} + +.emoji-button { + padding: 5px; + min-width: 60px; + height: 60px; + font-size: 1.5rem; + position: relative; +} + +.emoji-button.selected { + background-color: rgba(0, 255, 65, 0.2); + border: 2px solid var(--text-color); +} + +.emoji-button .tooltip { + visibility: hidden; + background-color: var(--button-bg); + color: var(--text-color); + text-align: center; + border-radius: 4px; + padding: 5px; + position: absolute; + z-index: 1; + bottom: 100%; + left: 50%; + transform: translateX(-50%); + white-space: nowrap; + font-size: 0.8rem; + opacity: 0; + transition: opacity 0.3s; +} + +.emoji-button:hover .tooltip { + visibility: visible; + opacity: 1; +} + +/* Transform buttons */ +.transform-buttons { + display: flex; + flex-wrap: wrap; + gap: 10px; + margin-bottom: 20px; +} + +.transform-button { + padding: 5px 10px; + margin: 2px; + height: auto; +} + +/* Copy button */ +.copy-button { + margin-top: 10px; + display: inline-flex; + align-items: center; + gap: 5px; +} + +/* Section dividers */ +.section-divider { + border-top: 1px solid var(--section-divider); + margin: 30px 0; +} + +/* GitHub link */ +.github-link { + display: inline-flex; + align-items: center; + gap: 5px; + color: var(--text-color); + text-decoration: none; + padding: 8px 16px; + border: 1px solid var(--button-border); + border-radius: 4px; + transition: all 0.3s; +} + +.github-link:hover { + background-color: var(--button-hover-bg); + color: var(--button-hover-text); +} + +/* Responsive adjustments */ +@media (max-width: 768px) { + .emoji-grid { + grid-template-columns: repeat(auto-fill, minmax(50px, 1fr)); + } + + .emoji-button { + min-width: 50px; + height: 50px; + } + + .tab-buttons { + flex-direction: column; + } + + .tab-buttons button { + width: 100%; + text-align: left; + } +} diff --git a/index.html b/index.html new file mode 100644 index 0000000..04dd94a --- /dev/null +++ b/index.html @@ -0,0 +1,145 @@ + + + + + + Parseltongue 2.0 - LLM Payload Crafter + + + + + + + +
+
+

๐Ÿ Parseltongue 2.0

+

LLM Payload Crafter

+
+ +
+
+ + + +
+ + +
+

โœจ Emoji Steganography

+
+ + +
+ +
+ +
+ +
+
+ +
+ + + +
+ +
+ +

๐Ÿ” Invisible Text

+
+ + +
+ +
+ + + +
+ +
+ +

๐Ÿ”Ž Universal Decoder

+
+ + +
+ +
+ + + +
+
+ + +
+

Text Transformation Tools

+
+ + +
+ +
+ +
+ +
+ + + +
+
+ + +
+

About Parseltongue 2.0

+

+ Parseltongue 2.0 is a tool for crafting and obfuscating text using various transformation and steganography techniques. + It's designed for educational purposes and to demonstrate the creative ways text can be manipulated. +

+
Features:
+
    +
  • Emoji Steganography - Hide messages within emoji using Unicode variation selectors
  • +
  • Invisible Text - Create text that's invisible but still present in the document
  • +
  • Text Transformations - Convert text to various formats like leetspeak, binary, morse code, etc.
  • +
  • Universal Decoder - Automatically detect and decode hidden messages
  • +
+

+ Note: This tool is for educational purposes only. Please use responsibly. +

+

+ + View on GitHub + +

+
+
+
+ + + + + + diff --git a/parsel_app.py b/parsel_app.py new file mode 100644 index 0000000..3c77664 --- /dev/null +++ b/parsel_app.py @@ -0,0 +1,498 @@ +import streamlit as st +import base64 +import pyperclip +from PIL import Image +import io +import zlib +import numpy as np +import re +import logging +import random +from typing import List, Dict, Optional +from string import ascii_lowercase + +# Import additional transformations +from more_transforms_fixed import ( + to_upside_down, to_elder_futhark, to_vaporwave, to_zalgo, + to_unicode_circled, to_small_caps, to_braille +) + +# Configure logging +logging.basicConfig( + level=logging.DEBUG, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' +) +logger = logging.getLogger('parseltongue') + +# Set page config with dark theme and wide layout +st.set_page_config( + page_title="Parseltongue 2.0", + page_icon="๐Ÿ", + layout="wide", + initial_sidebar_state="collapsed" +) + +# Custom CSS for dark hacker theme +st.markdown(""" + +""", unsafe_allow_html=True) + +# Helper Functions +def text_to_leetspeak(text: str) -> str: + """Convert text to leetspeak""" + leet_dict = { + 'a': '4', 'e': '3', 'i': '1', 'l': '1', + 'o': '0', 's': '5', 't': '7', 'b': '8', + 'g': '9', 'z': '2' + } + return ''.join(leet_dict.get(c.lower(), c) for c in text) + +def text_to_pig_latin(text: str) -> str: + """Convert text to Pig Latin""" + words = text.split() + result = [] + for word in words: + if word[0].lower() in 'aeiou': + result.append(word + 'way') + else: + consonants = '' + i = 0 + while i < len(word) and word[i].lower() not in 'aeiou': + consonants += word[i] + i += 1 + result.append(word[i:] + consonants + 'ay') + return ' '.join(result) + +def rot13(text: str) -> str: + """Apply ROT13 encoding""" + result = '' + for char in text: + if char.isalpha(): + ascii_offset = ord('a') if char.islower() else ord('A') + rotated = (ord(char) - ascii_offset + 13) % 26 + ascii_offset + result += chr(rotated) + else: + result += char + return result + +def to_base64(text: str) -> str: + """Convert text to Base64""" + if not text: + return "" + return base64.b64encode(text.encode('utf-8')).decode('utf-8') + +def to_binary(text: str) -> str: + """Convert text to binary""" + if not text: + return "" + return ' '.join(format(ord(c), '08b') for c in text) + +def to_hex(text: str) -> str: + """Convert text to hexadecimal""" + if not text: + return "" + return ' '.join(format(ord(c), '02x') for c in text) + +def to_bubble_text(text: str) -> str: + """Convert text to bubble text""" + if not text: + return "" + # Map for bubble text (circled latin letters) + bubble_map = { + 'a': '\u24d0', 'b': '\u24d1', 'c': '\u24d2', 'd': '\u24d3', 'e': '\u24d4', 'f': '\u24d5', 'g': '\u24d6', 'h': '\u24d7', 'i': '\u24d8', + 'j': '\u24d9', 'k': '\u24da', 'l': '\u24db', 'm': '\u24dc', 'n': '\u24dd', 'o': '\u24de', 'p': '\u24df', 'q': '\u24e0', 'r': '\u24e1', + 's': '\u24e2', 't': '\u24e3', 'u': '\u24e4', 'v': '\u24e5', 'w': '\u24e6', 'x': '\u24e7', 'y': '\u24e8', 'z': '\u24e9', + 'A': '\u24b6', 'B': '\u24b7', 'C': '\u24b8', 'D': '\u24b9', 'E': '\u24ba', 'F': '\u24bb', 'G': '\u24bc', 'H': '\u24bd', 'I': '\u24be', + 'J': '\u24bf', 'K': '\u24c0', 'L': '\u24c1', 'M': '\u24c2', 'N': '\u24c3', 'O': '\u24c4', 'P': '\u24c5', 'Q': '\u24c6', 'R': '\u24c7', + 'S': '\u24c8', 'T': '\u24c9', 'U': '\u24ca', 'V': '\u24cb', 'W': '\u24cc', 'X': '\u24cd', 'Y': '\u24ce', 'Z': '\u24cf', + '0': '\u24ea', '1': '\u2460', '2': '\u2461', '3': '\u2462', '4': '\u2463', '5': '\u2464', '6': '\u2465', '7': '\u2466', '8': '\u2467', '9': '\u2468', + ' ': ' ' + } + return ''.join(bubble_map.get(c, c) for c in text) + +def to_fullwidth(text: str) -> str: + """Convert text to fullwidth characters""" + if not text: + return "" + # Map for fullwidth text + fullwidth_map = { + 'a': '\uff41', 'b': '\uff42', 'c': '\uff43', 'd': '\uff44', 'e': '\uff45', 'f': '\uff46', 'g': '\uff47', 'h': '\uff48', 'i': '\uff49', + 'j': '\uff4a', 'k': '\uff4b', 'l': '\uff4c', 'm': '\uff4d', 'n': '\uff4e', 'o': '\uff4f', 'p': '\uff50', 'q': '\uff51', 'r': '\uff52', + 's': '\uff53', 't': '\uff54', 'u': '\uff55', 'v': '\uff56', 'w': '\uff57', 'x': '\uff58', 'y': '\uff59', 'z': '\uff5a', + 'A': '\uff21', 'B': '\uff22', 'C': '\uff23', 'D': '\uff24', 'E': '\uff25', 'F': '\uff26', 'G': '\uff27', 'H': '\uff28', 'I': '\uff29', + 'J': '\uff2a', 'K': '\uff2b', 'L': '\uff2c', 'M': '\uff2d', 'N': '\uff2e', 'O': '\uff2f', 'P': '\uff30', 'Q': '\uff31', 'R': '\uff32', + 'S': '\uff33', 'T': '\uff34', 'U': '\uff35', 'V': '\uff36', 'W': '\uff37', 'X': '\uff38', 'Y': '\uff39', 'Z': '\uff3a', + '0': '\uff10', '1': '\uff11', '2': '\uff12', '3': '\uff13', '4': '\uff14', '5': '\uff15', '6': '\uff16', '7': '\uff17', '8': '\uff18', '9': '\uff19', + ' ': '\u3000', '!': '\uff01', '?': '\uff1f', '.': '\uff0e', ',': '\uff0c', ';': '\uff1b', ':': '\uff1a', '(': '\uff08', ')': '\uff09', + '[': '\uff3b', ']': '\uff3d', '{': '\uff5b', '}': '\uff5d', '<': '\uff1c', '>': '\uff1e', '\\': '\uff3c', '/': '\uff0f', '|': '\uff5c', + '`': '\uff40', '~': '\uff5e', '@': '\uff20', '#': '\uff03', '$': '\uff04', '%': '\uff05', '^': '\uff3e', '&': '\uff06', '*': '\uff0a', + '-': '\uff0d', '_': '\uff3f', '+': '\uff0b', '=': '\uff1d', '"': '\uff02', '\'': '\uff07' + } + return ''.join(fullwidth_map.get(c, c) for c in text) + +def to_morse(text: str) -> str: + """Convert text to Morse code""" + if not text: + return "" + # Morse code mapping + morse_map = { + 'a': '.-', 'b': '-...', 'c': '-.-.', 'd': '-..', 'e': '.', 'f': '..-.', 'g': '--.', 'h': '....', + 'i': '..', 'j': '.---', 'k': '-.-', 'l': '.-..', 'm': '--', 'n': '-.', 'o': '---', 'p': '.--.', + 'q': '--.-', 'r': '.-.', 's': '...', 't': '-', 'u': '..-', 'v': '...-', 'w': '.--', 'x': '-..-', + 'y': '-.--', 'z': '--..', '0': '-----', '1': '.----', '2': '..---', '3': '...--', '4': '....-', + '5': '.....', '6': '-....', '7': '--...', '8': '---..', '9': '----.', ' ': '/' + } + return ' '.join(morse_map.get(c.lower(), c) for c in text) + +def to_binary_ascii(text: str) -> str: + """Convert text to ASCII binary (with ASCII values)""" + if not text: + return "" + return ' '.join(str(ord(c)) for c in text) + +def to_reverse(text: str) -> str: + """Reverse the text""" + if not text: + return "" + return text[::-1] + +# Define carriers for steganography with descriptions +CARRIERS = [ + {'emoji': '๐Ÿ', 'name': 'SNAKE', 'desc': 'Classic Snake'}, + {'emoji': '๐Ÿ‰', 'name': 'DRAGON', 'desc': 'Mystical Dragon'}, + {'emoji': '๐Ÿง™', 'name': 'WIZARD', 'desc': 'Powerful Wizard'}, + {'emoji': '๐Ÿ”ฎ', 'name': 'CRYSTAL', 'desc': 'Magic Crystal Ball'}, + {'emoji': 'โšก', 'name': 'LIGHTNING', 'desc': 'Lightning Bolt'}, + {'emoji': '๐ŸŒŸ', 'name': 'STAR', 'desc': 'Shining Star'}, + {'emoji': '๐ŸŽญ', 'name': 'MASK', 'desc': 'Theater Mask'}, + {'emoji': '๐Ÿ—๏ธ', 'name': 'KEY', 'desc': 'Ancient Key'}, + {'emoji': '๐Ÿ“œ', 'name': 'SCROLL', 'desc': 'Magic Scroll'}, + {'emoji': '๐Ÿ”’', 'name': 'LOCK', 'desc': 'Secure Lock'} +] + +def to_variation_selector(byte: int) -> str: + """Convert a byte to a variation selector character""" + return chr(0xFE00 + byte) + +def from_variation_selector(code_point: int) -> int: + """Convert a variation selector character back to a byte""" + return code_point - 0xFE00 + +def encode_emoji(emoji: str, text: str) -> str: + """Encode text using variation selectors""" + if not text: + return emoji + + # Convert text to binary + binary = ''.join(format(ord(c), '08b') for c in text) + + # Use variation selectors to encode binary + vs15, vs16 = '\ufe0e', '\ufe0f' + encoded = emoji + + for bit in binary: + encoded += vs15 if bit == '0' else vs16 + + return encoded + +def decode_emoji(text: str) -> str: + """Decode text from variation selectors""" + if not text: + return "" + + # Extract variation selectors + vs_pattern = r'[\ufe0e\ufe0f]' + matches = re.findall(vs_pattern, text) + + if not matches: + return "" + + # Convert variation selectors to binary + binary = ''.join('0' if vs == '\ufe0e' else '1' for vs in matches) + + # Convert binary to text + decoded = "" + for i in range(0, len(binary), 8): + byte = binary[i:i+8] + if len(byte) == 8: + decoded += chr(int(byte, 2)) + + return decoded + +def encode_invisible(text: str) -> str: + """Encode text using Tags Unicode block (U+E0000 to U+E007F)""" + if not text: + return "" + + result = '' + for c in text: + # Add the character as a Tags character + result += chr(0xE0000 + ord(c) % 0x7F) + # Add a space from the Tags block + result += chr(0xE0020) # This is the space character in Tags block + + return result + +def decode_invisible(text: str) -> str: + """Decode text from Tags Unicode block (U+E0000 to U+E007F)""" + if not text: + return "" + + result = '' + # Filter valid Tags characters + chars = [c for c in text if 0xE0000 <= ord(c) <= 0xE007F] + + for c in chars: + if ord(c) != 0xE0020: # Skip the space character + # Convert back from Tags block + original = chr((ord(c) - 0xE0000) % 128) + result += original + + return result + +def encode_image_steganography(image: Image.Image, message: str, plane: str = 'red') -> Image.Image: + """Encode a message into an image using LSB steganography""" + # Convert the image to RGB if it's not already + img = image.convert('RGB') + + # Get the image data as a numpy array + data = np.array(img) + + # Convert message to binary + binary = ''.join(format(ord(c), '08b') for c in message) + binary += '00000000' # Add null terminator + + # Get the correct color plane + plane_idx = {'red': 0, 'green': 1, 'blue': 2}[plane] + + # Encode the message + idx = 0 + for i in range(data.shape[0]): + for j in range(data.shape[1]): + if idx < len(binary): + # Clear the LSB and set it to the message bit + data[i, j, plane_idx] = (data[i, j, plane_idx] & 0xFE) | int(binary[idx]) + idx += 1 + + # Create a new image from the modified data + encoded_image = Image.fromarray(data) + return encoded_image + +# Main App +st.title("๐Ÿ Parseltongue 2.0") +st.markdown("""

LLM Payload Crafter

""", unsafe_allow_html=True) + +# Create tabs for steganography and decoder +tab1, tab2 = st.tabs(["๐Ÿ” Steganography", "๐Ÿ” Universal Decoder"]) + +# Steganography Tab +with tab1: + st.markdown("""

โœจ Emoji Steganography

""", unsafe_allow_html=True) + + # Text input for message + message = st.text_area("Enter your message:", key="emoji_message", placeholder="Type your message here...") + + if message: + st.markdown("""

Click an emoji to encode and copy to clipboard:

""", unsafe_allow_html=True) + + # Create a grid of emojis with their descriptions + cols = st.columns(8) # More columns for smaller buttons + for i, carrier in enumerate(CARRIERS): + with cols[i % 8]: + # Create a smaller button with just the emoji + button = st.button(carrier['emoji'], key=f"emoji_{i}", help=carrier['desc']) + st.markdown("
", unsafe_allow_html=True) + if button: + try: + # Encode the message + encoded = encode_emoji(carrier['emoji'], message) + # Copy to clipboard + pyperclip.copy(encoded) + # Show success message with preview + st.success(f"โœ… Encoded with {carrier['name']} and copied to clipboard!") + except Exception as e: + st.error(f"Error encoding message: {str(e)}") + + # Add a divider + st.markdown("
", unsafe_allow_html=True) + + # Omni-Encoder Section + st.markdown("""

๐Ÿ” Omni-Encoder

""", unsafe_allow_html=True) + st.markdown("""

Click to transform and copy to clipboard:

""", unsafe_allow_html=True) + + # Create a grid of transformation options - 4 columns, 4 rows + st.markdown("
", unsafe_allow_html=True) + # Use 4 rows of 4 columns each + for row in range(4): + transform_cols = st.columns(4) + + # Define transformations + transformations = [ + {"name": "Base64", "func": to_base64, "icon": "๐Ÿ”ข"}, + {"name": "Binary", "func": to_binary, "icon": "01"}, + {"name": "Hex", "func": to_hex, "icon": "0x"}, + {"name": "ASCII", "func": to_binary_ascii, "icon": "๐Ÿ”ค"}, + {"name": "ROT13", "func": rot13, "icon": "๐Ÿ”“"}, + {"name": "Leetspeak", "func": text_to_leetspeak, "icon": "1337"}, + {"name": "Pig Latin", "func": text_to_pig_latin, "icon": "๐Ÿท"}, + {"name": "Morse", "func": to_morse, "icon": "โ€ขโˆ’โ€ข"}, + {"name": "Bubble", "func": to_bubble_text, "icon": "โ“‘โ“คโ“‘"}, + {"name": "Fullwidth", "func": to_fullwidth, "icon": "๏ผฆ๏ผท"}, + {"name": "Reversed", "func": to_reverse, "icon": "โŸฒ"}, + {"name": "Upside Down", "func": to_upside_down, "icon": "๐Ÿ™ƒ"}, + {"name": "Runes", "func": to_elder_futhark, "icon": "แš แšขแšฆแšจแšฑแšฒ"}, + {"name": "Vaporwave", "func": to_vaporwave, "icon": "๏ฝ–๏ฝ๏ฝ๏ฝ๏ฝ’"}, + {"name": "Zalgo", "func": to_zalgo, "icon": "Zฬทฬขฬงอaฬถฬขอlฬธฬจฬ›อgฬตฬขฬงฬ›oฬตฬกอ˜"}, + {"name": "Circled", "func": to_unicode_circled, "icon": "๐Ÿ…’๐Ÿ…˜๐Ÿ…ก"}, + {"name": "Small Caps", "func": to_small_caps, "icon": "แด€ส™แด„"}, + {"name": "Braille", "func": to_braille, "icon": "โ ƒโ —โ โ Šโ ‡โ ‡โ ‘"} + ] + + for i, transform in enumerate(transformations): + row_idx = i // 4 # Determine which row this transform belongs to + col_idx = i % 4 # Determine which column in the row + + # Only process transforms for the current row + if row_idx < 4: # We have 4 rows total + with transform_cols[col_idx]: + # Create a button for each transformation + button = st.button(f"{transform['icon']} {transform['name']}", key=f"transform_{i}") + st.markdown("
", unsafe_allow_html=True) + if button: + try: + # Transform the message + transformed = transform['func'](message) + # Copy to clipboard + pyperclip.copy(transformed) + # Show success message with preview + st.success(f"โœ… {transform['name']} encoded and copied to clipboard!\n\nPreview: {transformed[:50] + '...' if len(transformed) > 50 else transformed}") + except Exception as e: + st.error(f"Error transforming message: {str(e)}") + + # Invisible Text Section + st.markdown("""

๐Ÿ‘ป Invisible Text

""", unsafe_allow_html=True) + invisible_input = st.text_area("Enter text to make invisible", key="invisible_input") + if invisible_input: + invisible_output = encode_invisible(invisible_input) + st.text_area("Invisible text (copied to clipboard)", invisible_output, height=100) + if st.button("๐Ÿ“‹ Copy Invisible Text", key="copy_invisible"): + pyperclip.copy(invisible_output) + st.success("โœ… Copied to clipboard!") + + # Image Steganography Section + st.markdown("""

๐Ÿ–ผ๏ธ Image Steganography

""", unsafe_allow_html=True) + col1, col2 = st.columns(2) + with col1: + uploaded_file = st.file_uploader("Choose carrier image", type=['png', 'jpg', 'jpeg']) + if uploaded_file: + image = Image.open(uploaded_file) + st.image(image, caption="Carrier Image") + + with col2: + steg_message = st.text_area("Enter message to hide", key="steg_message") + color_plane = st.selectbox("Select color plane", ["red", "green", "blue"]) + + if uploaded_file and steg_message and st.button("๐Ÿ”’ Encode Message"): + try: + encoded_image = encode_image_steganography(image, steg_message, color_plane) + st.image(encoded_image, caption="Encoded Image") + + # Save the image to a bytes buffer + buf = io.BytesIO() + encoded_image.save(buf, format='PNG') + byte_im = buf.getvalue() + + st.download_button( + label="๐Ÿ’พ Download Encoded Image", + data=byte_im, + file_name="encoded_image.png", + mime="image/png" + ) + except Exception as e: + st.error(f"Error encoding message in image: {str(e)}") + +# Universal Decoder Tab +with tab2: + st.markdown("### Text Transformation Tools") + text_input = st.text_area("Enter text to transform", key="obfuscate_input") + + if text_input: + # Create columns for different transformations + col1, col2 = st.columns(2) + + with col1: + st.markdown("#### Basic Transformations") + st.text_area("Leetspeak:", text_to_leetspeak(text_input)) + st.text_area("Pig Latin:", text_to_pig_latin(text_input)) + st.text_area("ROT13:", rot13(text_input)) + + with col2: + st.markdown("#### Decodings") + # Try to decode emoji + emoji_decoded = decode_emoji(text_input) + if emoji_decoded: + st.text_area("Emoji Decoded:", emoji_decoded) + + # Try to decode invisible text + invisible_decoded = decode_invisible(text_input) + if invisible_decoded: + st.text_area("Invisible Text Decoded:", invisible_decoded)