UI: Streamline interface and add keyboard shortcuts

- Simplified header and navigation
- Added keyboard shortcuts for transforms (1-9)
- Improved button styles and visual feedback
- Enhanced accessibility with ARIA labels
- Better focus states and visual hierarchy
This commit is contained in:
EP
2025-03-09 17:41:05 -04:00
parent 803ab0fd16
commit 92245931cd
2 changed files with 476 additions and 190 deletions
+329 -90
View File
@@ -1,14 +1,18 @@
/* Dark hacker theme styling */
/* Modern dark 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;
--main-bg-color: #1a1a1a;
--secondary-bg: #242424;
--text-color: #e0e0e0;
--accent-color: #64b5f6;
--accent-hover: #90caf9;
--success-color: #81c784;
--button-bg: #2d2d2d;
--button-hover-bg: #404040;
--button-active-bg: #64b5f6;
--input-bg: #2d2d2d;
--input-border: #404040;
--section-divider: #404040;
--focus-shadow: 0 0 0 2px rgba(100, 181, 246, 0.4);
}
* {
@@ -20,20 +24,56 @@
body {
background-color: var(--main-bg-color);
color: var(--text-color);
font-family: 'Courier New', monospace;
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
line-height: 1.6;
padding: 20px;
min-height: 100vh;
}
.container {
max-width: 1200px;
max-width: 900px;
margin: 0 auto;
padding: 20px;
padding: 24px;
background-color: var(--secondary-bg);
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
header {
text-align: center;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
padding: 10px;
}
.logo {
text-align: left;
}
.theme-toggle {
margin-left: 20px;
}
.theme-button {
background: transparent;
border: none;
color: var(--text-color);
font-size: 1.2rem;
padding: 8px;
cursor: pointer;
border-radius: 50%;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
}
.theme-button:hover {
background: var(--button-bg);
transform: rotate(15deg);
}
h1, h2, h3, h4, h5 {
@@ -44,48 +84,84 @@ h1, h2, h3, h4, h5 {
/* Tabs styling */
.tabs {
margin-top: 20px;
margin-top: 16px;
}
.tab-buttons {
display: flex;
margin-bottom: 20px;
border-bottom: 1px solid var(--text-color);
gap: 8px;
margin-bottom: 24px;
}
.tab-buttons button {
background-color: transparent;
background-color: var(--button-bg);
color: var(--text-color);
border: none;
padding: 10px 20px;
border: 1px solid var(--input-border);
border-radius: 4px;
padding: 8px 16px;
cursor: pointer;
font-size: 16px;
font-family: 'Courier New', monospace;
transition: all 0.3s;
font-size: 14px;
display: flex;
align-items: center;
gap: 8px;
transition: all 0.2s ease;
}
.tab-buttons button i {
font-size: 14px;
}
.tab-buttons button.active {
border-bottom: 3px solid var(--text-color);
font-weight: bold;
background-color: var(--accent-color);
color: var(--main-bg-color);
border-color: var(--accent-color);
font-weight: 500;
}
.tab-buttons button:hover {
background-color: rgba(0, 255, 65, 0.1);
.tab-buttons button:hover:not(.active) {
background-color: var(--button-hover-bg);
border-color: var(--accent-color);
color: var(--accent-color);
}
.transform-button.active {
background-color: var(--accent-color);
color: var(--main-bg-color);
border-color: var(--accent-color);
}
.transform-button:hover:not(.active) {
background-color: var(--button-hover-bg);
border-color: var(--accent-color);
color: var(--accent-color);
}
.tab-content {
padding: 20px 0;
}
/* Form elements */
.form-group {
margin-bottom: 20px;
/* Input and output sections */
.input-section,
.output-section,
.decode-section {
background: var(--main-bg-color);
border-radius: 4px;
padding: 16px;
margin-bottom: 16px;
}
label {
display: block;
margin-bottom: 8px;
font-weight: bold;
.input-container,
.output-container {
position: relative;
}
.decoded-message {
margin-top: 8px;
padding: 8px;
background: var(--button-bg);
border-radius: 4px;
font-family: 'Fira Code', monospace;
position: relative;
}
textarea {
@@ -95,9 +171,32 @@ textarea {
color: var(--text-color);
border: 1px solid var(--input-border);
border-radius: 4px;
font-family: 'Courier New', monospace;
font-family: 'Fira Code', 'Courier New', monospace;
resize: vertical;
min-height: 100px;
line-height: 1.4;
font-size: 14px;
transition: all 0.2s ease;
}
textarea::placeholder {
color: rgba(224, 224, 224, 0.5);
}
textarea:focus {
outline: none;
border-color: var(--accent-color);
box-shadow: var(--focus-shadow);
}
button:focus-visible {
outline: none;
border-color: var(--accent-color);
box-shadow: var(--focus-shadow);
}
button:active {
transform: translateY(1px);
}
button {
@@ -111,75 +210,198 @@ button {
transition: all 0.3s;
}
.copy-button {
position: absolute;
top: 8px;
right: 8px;
padding: 6px;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
background: var(--button-bg);
border: 1px solid var(--input-border);
border-radius: 4px;
color: var(--text-color);
cursor: pointer;
transition: all 0.2s ease;
}
.copy-button:hover {
background: var(--button-hover-bg);
border-color: var(--accent-color);
color: var(--accent-color);
}
.copy-button:active {
transform: translateY(1px);
}
button:hover {
background-color: var(--button-hover-bg);
color: var(--button-hover-text);
}
/* Emoji grid */
.emoji-grid {
/* Transform layout */
.transform-layout {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(60px, 1fr));
gap: 10px;
margin-top: 10px;
gap: 24px;
margin-top: 16px;
}
.emoji-button {
padding: 5px;
min-width: 60px;
height: 60px;
font-size: 1.5rem;
position: relative;
.transform-section {
background: var(--main-bg-color);
border-radius: 8px;
padding: 16px;
border: 1px solid var(--input-border);
}
.emoji-button.selected {
background-color: rgba(0, 255, 65, 0.2);
border: 2px solid var(--text-color);
.transform-section:focus-within {
border-color: var(--accent-color);
box-shadow: var(--focus-shadow);
}
.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;
.input-section,
.transform-section,
.output-section {
background: var(--main-bg-color);
border-radius: 6px;
padding: 16px;
}
.emoji-button:hover .tooltip {
visibility: visible;
opacity: 1;
}
/* Transform buttons */
.transform-buttons {
.transform-section h4 {
margin-bottom: 16px;
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-bottom: 20px;
align-items: center;
gap: 8px;
color: var(--accent-color);
}
.transform-buttons {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
gap: 12px;
}
.transform-button kbd {
position: absolute;
top: 4px;
right: 4px;
background: rgba(255, 255, 255, 0.1);
border-radius: 3px;
padding: 2px 4px;
font-size: 10px;
font-family: 'Fira Code', monospace;
pointer-events: none;
}
.transform-preview {
font-size: 11px;
opacity: 0.7;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.transform-button {
padding: 5px 10px;
margin: 2px;
height: auto;
display: flex;
flex-direction: column;
gap: 4px;
padding: 10px;
text-align: left;
border-radius: 4px;
background: var(--button-bg);
border: 1px solid var(--input-border);
transition: all 0.2s ease;
}
/* Copy button */
.copy-button {
margin-top: 10px;
display: inline-flex;
.transform-name {
font-weight: 500;
color: var(--text-color);
}
.transform-preview {
font-size: 0.75rem;
opacity: 0.6;
font-family: 'Fira Code', monospace;
color: var(--accent-color);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 140px;
}
.transform-button {
padding: 12px;
height: auto;
border-radius: 6px;
font-size: 0.9rem;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
gap: 5px;
gap: 4px;
min-width: 120px;
font-weight: 500;
text-align: center;
border: 1px solid var(--input-border);
background: var(--button-bg);
color: var(--text-color);
transition: all 0.2s ease;
}
.transform-button:hover {
background: var(--button-hover-bg);
color: var(--main-bg-color);
transform: translateY(-1px);
}
.transform-button.active {
background: var(--button-active-bg);
color: var(--main-bg-color);
border-color: var(--button-active-bg);
}
/* Output section */
.output-container {
position: relative;
}
.copy-button {
position: absolute;
top: 8px;
right: 8px;
padding: 8px;
background: var(--button-bg);
border: none;
border-radius: 6px;
color: var(--text-color);
opacity: 0.7;
transition: all 0.2s ease;
}
.copy-button:hover {
opacity: 1;
background: var(--button-hover-bg);
color: var(--main-bg-color);
}
.form-group label {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 12px;
color: var(--accent-color);
font-weight: 500;
}
.form-group label i {
font-size: 1.1rem;
}
/* Section dividers */
@@ -189,16 +411,33 @@ button:hover {
}
/* GitHub link */
.github-link {
display: inline-flex;
align-items: center;
gap: 5px;
.actions {
display: flex;
gap: 8px;
}
.theme-button,
.github-button {
background: var(--button-bg);
color: var(--text-color);
text-decoration: none;
padding: 8px 16px;
border: 1px solid var(--button-border);
border: 1px solid var(--input-border);
border-radius: 4px;
transition: all 0.3s;
padding: 8px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
text-decoration: none;
width: 32px;
height: 32px;
}
.theme-button:hover,
.github-button:hover {
background: var(--button-hover-bg);
border-color: var(--accent-color);
transform: translateY(-1px);
}
.github-link:hover {
+147 -100
View File
@@ -13,128 +13,175 @@
<body>
<div id="app" class="container">
<header>
<h1>🐍 Parseltongue 2.0</h1>
<h3>LLM Payload Crafter</h3>
<div class="logo">
<h1>🐍 Parseltongue</h1>
</div>
<div class="actions">
<button
@click="toggleTheme"
@keyup.d="toggleTheme"
class="theme-button"
title="Toggle dark mode (D)"
aria-label="Toggle dark mode"
>
<i class="fas" :class="isDarkTheme ? 'fa-moon' : 'fa-sun'"></i>
</button>
<a
href="https://github.com/elder-plinius/P4RS3LT0NGV3"
target="_blank"
class="github-button"
title="View source on GitHub"
aria-label="View source code on GitHub"
>
<i class="fab fa-github"></i>
</a>
</div>
</header>
<div class="tabs">
<div class="tab-buttons">
<button :class="{ active: activeTab === 'steganography' }" @click="activeTab = 'steganography'">Steganography</button>
<button :class="{ active: activeTab === 'transforms' }" @click="activeTab = 'transforms'">Text Transforms</button>
<button :class="{ active: activeTab === 'about' }" @click="activeTab = 'about'">About</button>
<button
:class="{ active: activeTab === 'transforms' }"
@click="activeTab = 'transforms'"
@keyup.t="activeTab = 'transforms'"
title="Transform text (T)"
>
<i class="fas fa-font"></i> Transform
</button>
<button
:class="{ active: activeTab === 'steganography' }"
@click="activeTab = 'steganography'"
@keyup.h="activeTab = 'steganography'"
title="Hide text (H)"
>
<i class="fas fa-eye-slash"></i> Hide
</button>
</div>
<!-- Steganography Tab -->
<div v-if="activeTab === 'steganography'" class="tab-content">
<h4>✨ Emoji Steganography</h4>
<div class="form-group">
<label for="emoji-message">Enter your message:</label>
<textarea id="emoji-message" v-model="emojiMessage" placeholder="Type your message here..."></textarea>
</div>
<div class="form-group">
<label>Select an emoji carrier:</label>
<div class="emoji-grid">
<button
v-for="carrier in carriers"
:key="carrier.name"
class="emoji-button"
@click="selectCarrier(carrier)"
:class="{ selected: selectedCarrier === carrier }"
>
{{ carrier.emoji }}
<span class="tooltip">{{ carrier.desc }}</span>
</button>
<div class="transform-layout">
<div class="input-section">
<textarea
id="steg-input"
v-model="emojiMessage"
placeholder="Enter text to hide..."
@input="autoEncode"
></textarea>
</div>
</div>
<div class="form-group" v-if="selectedCarrier && emojiMessage">
<label>Encoded Message:</label>
<textarea readonly v-model="encodedEmojiMessage"></textarea>
<button class="copy-button" @click="copyToClipboard(encodedEmojiMessage)">
<i class="fas fa-clipboard"></i> Copy
</button>
</div>
<div class="transform-section">
<div class="transform-buttons">
<button
v-for="carrier in carriers"
:key="carrier.name"
class="transform-button"
:class="{ active: selectedCarrier === carrier }"
@click="selectCarrier(carrier)"
:title="carrier.desc"
>
{{ carrier.emoji }} {{ carrier.name }}
<small class="transform-preview" v-if="emojiMessage">
{{ carrier.preview ? carrier.preview(emojiMessage.slice(0, 10)) : '' }}
</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>
</div>
<div class="section-divider"></div>
<div class="output-section" v-if="encodedMessage">
<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>
<h4>🔍 Invisible Text</h4>
<div class="form-group">
<label for="invisible-input">Enter text to make invisible:</label>
<textarea id="invisible-input" v-model="invisibleInput" placeholder="Type your message here..."></textarea>
</div>
<div class="form-group" v-if="invisibleInput">
<label>Invisible Text:</label>
<textarea readonly v-model="invisibleOutput"></textarea>
<button class="copy-button" @click="copyToClipboard(invisibleOutput)">
<i class="fas fa-clipboard"></i> Copy
</button>
</div>
<div class="section-divider"></div>
<h4>🔎 Universal Decoder</h4>
<div class="form-group">
<label for="decode-input">Enter text to decode:</label>
<textarea id="decode-input" v-model="decodeInput" placeholder="Paste encoded text here..."></textarea>
</div>
<div class="form-group" v-if="decodedMessage">
<label>Decoded Message:</label>
<textarea readonly v-model="decodedMessage"></textarea>
<button class="copy-button" @click="copyToClipboard(decodedMessage)">
<i class="fas fa-clipboard"></i> Copy
</button>
<div class="decode-section" v-if="showDecoder">
<div class="input-container">
<textarea
id="decode-input"
v-model="decodeInput"
placeholder="Paste encoded text to decode..."
@input="autoDecode"
></textarea>
<div class="decoded-message" v-if="decodedMessage">
{{ decodedMessage }}
<button class="copy-button" @click="copyToClipboard(decodedMessage)" title="Copy decoded text">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Text Transforms Tab -->
<div v-if="activeTab === 'transforms'" class="tab-content">
<h4>Text Transformation Tools</h4>
<div class="form-group">
<label for="transform-input">Enter text to transform:</label>
<textarea id="transform-input" v-model="transformInput" placeholder="Type your text here..."></textarea>
</div>
<div class="transform-layout">
<div class="input-section">
<textarea
id="transform-input"
v-model="transformInput"
placeholder="Enter text to transform..."
@input="autoTransform"
></textarea>
</div>
<div class="transform-buttons">
<button v-for="transform in transforms" :key="transform.name" @click="applyTransform(transform.func)" class="transform-button">
{{ transform.name }}
</button>
</div>
<div class="transform-section">
<div class="transform-buttons">
<button
v-for="(transform, index) in transforms"
:key="transform.name"
@click="applyTransform(transform.func)"
@keyup="index < 9 ? applyTransform(transform.func) : null"
class="transform-button"
:class="{ active: activeTransform === transform.name }"
:title="transform.name + (index < 9 ? ' (' + (index + 1) + ')' : '')"
:data-shortcut="index < 9 ? index + 1 : ''"
>
{{ transform.name }}
<small class="transform-preview" v-if="transformInput">
{{ transform.preview ? transform.preview(transformInput.slice(0, 10)) : '' }}
</small>
<kbd v-if="index < 9">{{ index + 1 }}</kbd>
</button>
</div>
</div>
<div class="form-group" v-if="transformOutput">
<label>Transformed Text:</label>
<textarea readonly v-model="transformOutput"></textarea>
<button class="copy-button" @click="copyToClipboard(transformOutput)">
<i class="fas fa-clipboard"></i> Copy
</button>
<div class="output-section" v-if="transformOutput">
<div class="output-container">
<textarea
readonly
v-model="transformOutput"
aria-label="Transformed text output"
></textarea>
<button
class="copy-button"
@click="copyToClipboard(transformOutput)"
@keyup.c="copyToClipboard(transformOutput)"
title="Copy to clipboard (C)"
aria-label="Copy transformed text to clipboard"
>
<i class="fas fa-copy"></i>
</button>
</div>
</div>
</div>
</div>
<!-- About Tab -->
<div v-if="activeTab === 'about'" class="tab-content">
<h4>About Parseltongue 2.0</h4>
<p>
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.
</p>
<h5>Features:</h5>
<ul>
<li>Emoji Steganography - Hide messages within emoji using Unicode variation selectors</li>
<li>Invisible Text - Create text that's invisible but still present in the document</li>
<li>Text Transformations - Convert text to various formats like leetspeak, binary, morse code, etc.</li>
<li>Universal Decoder - Automatically detect and decode hidden messages</li>
</ul>
<p>
<strong>Note:</strong> This tool is for educational purposes only. Please use responsibly.
</p>
<p>
<a href="https://github.com/yourusername/parseltongue" target="_blank" class="github-link">
<i class="fab fa-github"></i> View on GitHub
</a>
</p>
</div>
</div>
</div>