mirror of
https://github.com/elder-plinius/P4RS3LT0NGV3.git
synced 2026-04-22 03:36:19 +02:00
1007 lines
62 KiB
HTML
1007 lines
62 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Parseltongue 2.0 - LLM Payload Crafter</title>
|
||
<link rel="stylesheet" href="css/style.css">
|
||
<link rel="stylesheet" href="css/notification.css">
|
||
<!-- Vue.js -->
|
||
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
|
||
<!-- Font Awesome for icons -->
|
||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
|
||
</head>
|
||
<body>
|
||
<div id="app" class="container">
|
||
<header>
|
||
<div class="logo">
|
||
<h1>🐉️︎︎︎︎︎︎︎️︎︎︎️︎︎︎️︎️️︎️️︎︎︎︎︎︎︎️︎︎︎️︎︎︎️︎️️︎️️︎︎︎︎︎︎︎️︎︎︎️︎︎︎️︎️️︎️️︎︎︎︎︎︎︎️︎︎︎️︎︎︎️︎️️︎️︎︎️️️️︎︎︎️️️️️︎︎︎️︎︎︎️️️︎️︎︎️️️️︎️︎︎︎️︎︎︎️︎︎️️︎️︎️︎︎️️️️︎️︎︎︎️︎︎︎️︎︎︎️︎️︎︎️️️︎️︎︎️︎︎︎️︎️︎️︎︎️️️︎︎️︎︎︎︎︎️︎️︎︎︎︎️︎︎️︎︎️️︎︎︎️︎︎︎️︎️︎️︎︎︎️︎︎︎︎️︎️️️︎︎︎️︎️️️︎︎︎️︎️️️︎︎️︎︎️️︎︎︎️︎︎️️️️︎️︎️︎️️︎︎️︎︎︎️︎️︎︎️️️︎️︎︎️︎️︎︎︎︎︎️︎︎️️︎︎︎️︎︎️︎︎️︎️︎︎️️️︎︎️︎️️︎︎️︎️️️️️︎︎︎︎️️️️️︎︎︎️︎️️︎️️︎︎︎︎︎︎︎️︎︎︎️︎︎︎️︎️️︎️️︎︎︎︎︎︎︎️︎︎︎️︎︎︎️︎️️︎️️︎︎︎︎︎︎︎️︎︎︎️︎︎︎️︎️️︎️️︎︎︎︎︎︎︎️︎︎︎️︎ P4RS3LT0NGV3</h1>
|
||
</div>
|
||
<div class="actions">
|
||
<button
|
||
@click="toggleCopyHistory"
|
||
class="history-button"
|
||
title="Show copy history"
|
||
aria-label="Show copy history"
|
||
>
|
||
<i class="fas fa-history"></i>
|
||
</button>
|
||
<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>
|
||
<button
|
||
@click="toggleUnicodePanel"
|
||
class="history-button"
|
||
title="Advanced Unicode options"
|
||
aria-label="Advanced Unicode options"
|
||
>
|
||
<i class="fas fa-sliders-h"></i>
|
||
</button>
|
||
</div>
|
||
</header>
|
||
|
||
<div class="tabs">
|
||
<div class="tab-buttons">
|
||
<button
|
||
:class="{ active: activeTab === 'transforms' }"
|
||
@click="switchToTab('transforms')"
|
||
title="Transform text (T)"
|
||
>
|
||
<i class="fas fa-font"></i> Transform
|
||
</button>
|
||
<button
|
||
:class="{ active: activeTab === 'steganography' }"
|
||
@click="switchToTab('steganography')"
|
||
title="Hide text in emojis (H)"
|
||
>
|
||
<i class="fas fa-smile"></i> Emoji
|
||
</button>
|
||
<button
|
||
:class="{ active: activeTab === 'tokenade' }"
|
||
@click="switchToTab('tokenade')"
|
||
title="Tokenade Generator"
|
||
>
|
||
<i class="fas fa-bomb"></i> Tokenade
|
||
</button>
|
||
<button
|
||
:class="{ active: activeTab === 'tokenizer' }"
|
||
@click="switchToTab('tokenizer')"
|
||
title="Tokenizer visualization"
|
||
>
|
||
<i class="fas fa-layer-group"></i> Tokenizer
|
||
</button>
|
||
</div>
|
||
|
||
<!-- Steganography Tab -->
|
||
<div v-if="activeTab === 'steganography'" class="tab-content">
|
||
<div class="transform-layout">
|
||
<div class="input-section">
|
||
<textarea
|
||
id="steg-input"
|
||
v-model="emojiMessage"
|
||
placeholder="Enter text to hide..."
|
||
@input="autoEncode"
|
||
></textarea>
|
||
</div>
|
||
|
||
<!-- Emoji Library Section -->
|
||
<div class="emoji-library">
|
||
|
||
<!-- Emoji grid container - this MUST have id="emoji-grid-container" -->
|
||
<div id="emoji-grid-container" class="emoji-grid-container">
|
||
<!-- Dynamic content will be inserted here by JavaScript -->
|
||
<div class="emoji-grid">
|
||
<!-- Common emojis as fallback -->
|
||
<button class="emoji-button" onclick="app.selectEmoji('😀')">😀</button>
|
||
<button class="emoji-button" onclick="app.selectEmoji('😂')">😂</button>
|
||
<button class="emoji-button" onclick="app.selectEmoji('🥰')">🥰</button>
|
||
<button class="emoji-button" onclick="app.selectEmoji('😎')">😎</button>
|
||
<button class="emoji-button" onclick="app.selectEmoji('🤔')">🤔</button>
|
||
<button class="emoji-button" onclick="app.selectEmoji('👍')">👍</button>
|
||
<button class="emoji-button" onclick="app.selectEmoji('🎉')">🎉</button>
|
||
<button class="emoji-button" onclick="app.selectEmoji('🔥')">🔥</button>
|
||
<button class="emoji-button" onclick="app.selectEmoji('🚀')">🚀</button>
|
||
<button class="emoji-button" onclick="app.selectEmoji('🐍')">🐍</button>
|
||
</div>
|
||
</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>
|
||
|
||
|
||
<!-- Universal Decoder Section for Steganography Tab -->
|
||
<div class="decode-section">
|
||
<div class="section-header">
|
||
<h3>
|
||
<i class="fas fa-magic"></i> Universal Decoder
|
||
<small v-if="activeTransform">(Prioritizing {{ activeTransform.name }})</small>
|
||
</h3>
|
||
<p v-if="activeTransform && transformHasReverse(activeTransform)">
|
||
Paste encoded text to decode with {{ activeTransform.name }} or try other methods
|
||
</p>
|
||
<p v-else>Paste any encoded text to try all decoding methods at once</p>
|
||
</div>
|
||
|
||
<div class="input-container">
|
||
<textarea
|
||
id="universal-decode-input-steg"
|
||
v-model="universalDecodeInput"
|
||
placeholder="Paste encoded text to decode automatically..."
|
||
@input="runUniversalDecode"
|
||
></textarea>
|
||
<div class="decoded-message" v-if="universalDecodeResult">
|
||
<div class="decode-method">
|
||
<span>Decoded using: <strong>{{ universalDecodeResult.method }}</strong></span>
|
||
<span v-if="activeTransform && universalDecodeResult.method === activeTransform.name" class="decode-priority">(Priority Match)</span>
|
||
</div>
|
||
<div class="decode-result">{{ universalDecodeResult.text }}</div>
|
||
<div class="decode-actions">
|
||
<button class="copy-button" @click="copyToClipboard(universalDecodeResult.text)" title="Copy decoded text">
|
||
<i class="fas fa-copy"></i> Copy
|
||
</button>
|
||
<button class="use-as-input-button" v-if="universalDecodeResult.text" @click="transformInput = universalDecodeResult.text" title="Use this as input for transforms">
|
||
<i class="fas fa-arrow-up"></i> Use as Input
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Tokenizer Visualization Tab -->
|
||
<div v-if="activeTab === 'tokenizer'" class="tab-content">
|
||
<div class="transform-layout">
|
||
<div class="input-section">
|
||
<div class="section-header">
|
||
<h3><i class="fas fa-layer-group"></i> Tokenizer Visualization <small>{{ tokenizerEngine }}</small></h3>
|
||
<p>Paste text to see how different tokenizers segment it.</p>
|
||
</div>
|
||
<div class="options-grid" style="margin-bottom:8px;">
|
||
<label>
|
||
Engine
|
||
<select v-model="tokenizerEngine" @change="runTokenizer">
|
||
<option value="byte">UTF-8 bytes</option>
|
||
<option value="word">Naive words</option>
|
||
<option value="gpt3">Experimental: gpt-3-encoder (if available)</option>
|
||
</select>
|
||
</label>
|
||
</div>
|
||
<textarea
|
||
id="tokenizer-input"
|
||
v-model="tokenizerInput"
|
||
placeholder="Paste text to visualize tokens"
|
||
@input="runTokenizer"
|
||
></textarea>
|
||
</div>
|
||
|
||
<div class="output-section">
|
||
<div class="output-heading">
|
||
<h4>
|
||
<i class="fas fa-chart-bar"></i> Tokens
|
||
<small>{{ tokenizerTokens.length }} total · {{ tokenizerWordCount }} words · {{ tokenizerCharCount }} chars</small>
|
||
</h4>
|
||
</div>
|
||
<div class="token-tiles" v-if="tokenizerTokens.length" style="display:flex; flex-wrap:wrap; gap:6px;">
|
||
<div v-for="(tok, i) in tokenizerTokens" :key="i" class="token-chip" style="background:var(--button-bg); border:1px solid var(--input-border); padding:6px 8px; border-radius:6px; font-family:'Fira Code', monospace;">
|
||
<span style="opacity:.75; margin-right:6px;">{{ i }}</span>
|
||
<span>{{ tok.text }}</span>
|
||
<span v-if="tok.id !== undefined" style="opacity:.6; margin-left:6px;">#{{ tok.id }}</span>
|
||
</div>
|
||
</div>
|
||
<div class="output-instructions" v-else>
|
||
<small>Tokens will appear here.</small>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Tokenade Tab -->
|
||
<div v-if="activeTab === 'tokenade'" class="tab-content">
|
||
<div class="transform-layout">
|
||
<div class="token-bomb-section">
|
||
<div class="section-header">
|
||
<h3>
|
||
<span title="High-density token payload builder">💥 Tokenade Generator</span>
|
||
<small>Craft dense token sequences with emojis and zero-width characters</small>
|
||
</h3>
|
||
</div>
|
||
<div class="output-instructions">
|
||
<small>
|
||
<strong>Disclaimer:</strong> These payloads can be disruptive to models, UIs, and tools. Use responsibly and avoid deploying in production or against systems without consent.
|
||
</small>
|
||
</div>
|
||
<div class="tokenade-presets">
|
||
<button class="transform-button" title="Very light density" @click="applyTokenadePreset('feather')">🪶 Featherweight</button>
|
||
<button class="transform-button" title="Light density" @click="applyTokenadePreset('light')">🍃 Lightweight</button>
|
||
<button class="transform-button" title="Balanced density" @click="applyTokenadePreset('middle')">🪨 Middleweight</button>
|
||
<button class="transform-button" title="High density" @click="applyTokenadePreset('heavy')">🗿 Heavyweight</button>
|
||
<button class="transform-button" title="Extreme density (use with care)" @click="applyTokenadePreset('super')">⚓ Super Heavyweight</button>
|
||
</div>
|
||
<div class="token-bomb-controls options-grid hacker-controls">
|
||
<label class="slider-block" title="Nesting levels; higher = more layers of grouping">
|
||
Depth (nesting)
|
||
<input class="hacker-slider" type="range" v-model.number="tbDepth" min="1" max="8" />
|
||
<span class="slider-value">{{ tbDepth }}</span>
|
||
</label>
|
||
<label class="slider-block" title="How many items per level; higher = wider structure">
|
||
Breadth per level
|
||
<input class="hacker-slider" type="range" v-model.number="tbBreadth" min="1" max="12" />
|
||
<span class="slider-value">{{ tbBreadth }}</span>
|
||
</label>
|
||
<label class="slider-block" title="How many times to repeat the whole block">
|
||
Repeats (blocks)
|
||
<input class="hacker-slider" type="range" v-model.number="tbRepeats" min="1" max="100" />
|
||
<span class="slider-value">{{ tbRepeats }}</span>
|
||
</label>
|
||
<div class="segmented" title="Invisible separator inserted between units">
|
||
<div class="segmented-label">Separator</div>
|
||
<button :class="{active: tbSeparator==='zwj'}" @click="tbSeparator='zwj'">ZWJ</button>
|
||
<button :class="{active: tbSeparator==='zwnj'}" @click="tbSeparator='zwnj'">ZWNJ</button>
|
||
<button :class="{active: tbSeparator==='zwsp'}" @click="tbSeparator='zwsp'">ZWSP</button>
|
||
<button :class="{active: tbSeparator==='none'}" @click="tbSeparator='none'">None</button>
|
||
</div>
|
||
<label class="switch" title="Adds VS16/VS15 after glyphs to increase token churn">
|
||
<input type="checkbox" v-model="tbIncludeVS" />
|
||
<span>Variation Selectors</span>
|
||
</label>
|
||
<label class="switch" title="Sprinkles random zero-width codepoints between segments">
|
||
<input type="checkbox" v-model="tbIncludeNoise" />
|
||
<span>Invisible Noise</span>
|
||
</label>
|
||
<label class="switch" title="Use random emojis instead of cycling a fixed set">
|
||
<input type="checkbox" v-model="tbRandomizeEmojis" />
|
||
<span>Randomize</span>
|
||
</label>
|
||
<label class="switch" title="Automatically copy result after generation">
|
||
<input type="checkbox" v-model="tbAutoCopy" />
|
||
<span>Auto-copy</span>
|
||
</label>
|
||
</div>
|
||
<div class="output-instructions">
|
||
<small>
|
||
Estimated length: {{ estimateTokenadeLength().toLocaleString() }} chars
|
||
</small>
|
||
</div>
|
||
<div class="tokenade-carrier-options options-grid">
|
||
<div class="carrier-quick-grid" v-if="tbSingleCarrier">
|
||
<small>Quick picks</small>
|
||
<div class="emoji-grid" style="grid-template-columns: repeat(10, 32px); grid-auto-rows: 32px; gap: 6px;">
|
||
<button class="emoji-button" v-for="em in quickCarrierEmojis" :key="'q-'+em" @click="tbCarrier = em" :title="'Use '+em">{{ em }}</button>
|
||
</div>
|
||
</div>
|
||
<label title="Condense payload into a single emoji carrier">
|
||
Single emoji carrier mode
|
||
<input type="checkbox" v-model="tbSingleCarrier" />
|
||
</label>
|
||
<label v-if="tbSingleCarrier" title="Choose the carrier emoji (quick list)">
|
||
Carrier
|
||
<select v-model="tbCarrier" @focus="setCarrierFromSelected">
|
||
<option v-for="em in carrierEmojiList" :key="em" :value="em">{{ em }}</option>
|
||
</select>
|
||
</label>
|
||
<label v-if="tbSingleCarrier" title="Advanced: use this symbol or string as the embedded payload for encoding">
|
||
Advanced: custom carrier text
|
||
<input type="text" v-model="tbCarrierManual" placeholder="e.g., ⚙️ or ::tag::" />
|
||
<small>When set, this overrides quick picks and dropdown.</small>
|
||
</label>
|
||
</div>
|
||
<div class="token-bomb-actions">
|
||
<button class="transform-button" @click="generateTokenBomb" title="Build the tokenade with current settings">
|
||
<i class="fas fa-hammer"></i> Generate Tokenade
|
||
</button>
|
||
<button class="copy-button" v-if="tokenBombOutput" @click="copyToClipboard(tokenBombOutput)">
|
||
<i class="fas fa-copy"></i> Copy
|
||
</button>
|
||
<div class="output-instructions" v-if="tokenBombOutput">
|
||
<small>
|
||
Length: {{ tokenBombOutput.length.toLocaleString() }} chars · Tip: Increasing depth/breadth grows size <em>multiplicatively</em>.
|
||
</small>
|
||
</div>
|
||
</div>
|
||
<div class="output-container" v-if="tokenBombOutput">
|
||
<textarea readonly v-model="tokenBombOutput" aria-label="Token bomb output"></textarea>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Text Payload Generator (simple) -->
|
||
<div class="transform-section">
|
||
<h4><i class="fas fa-font"></i> Text Payload Generator</h4>
|
||
<div class="options-grid">
|
||
<label>
|
||
Base text
|
||
<input type="text" v-model="tpBase" placeholder="Enter base snippet (e.g., hello)" />
|
||
</label>
|
||
<label>
|
||
Repeat count
|
||
<input type="number" v-model.number="tpRepeat" min="1" max="1000" />
|
||
</label>
|
||
<label>
|
||
Add combining marks
|
||
<input type="checkbox" v-model="tpCombining" />
|
||
</label>
|
||
<label>
|
||
Add zero-width spacing
|
||
<input type="checkbox" v-model="tpZW" />
|
||
</label>
|
||
</div>
|
||
<div class="token-bomb-actions">
|
||
<button class="transform-button" @click="generateTextPayload">
|
||
<i class="fas fa-hammer"></i> Generate Text Payload
|
||
</button>
|
||
<button class="copy-button" v-if="textPayload" @click="copyToClipboard(textPayload)">
|
||
<i class="fas fa-copy"></i> Copy
|
||
</button>
|
||
</div>
|
||
<div class="output-container" v-if="textPayload">
|
||
<textarea readonly v-model="textPayload" aria-label="Text payload output"></textarea>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- Text Transforms Tab -->
|
||
<div v-if="activeTab === 'transforms'" class="tab-content">
|
||
<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-section">
|
||
<div class="transform-category-legend">
|
||
<div class="legend-title">Categories:</div>
|
||
<div class="legend-item transform-category-encoding" data-target="category-encoding">Encoding</div>
|
||
<div class="legend-item transform-category-cipher" data-target="category-cipher">Ciphers</div>
|
||
<div class="legend-item transform-category-visual" data-target="category-visual">Visual</div>
|
||
<div class="legend-item transform-category-format" data-target="category-format">Formatting</div>
|
||
<div class="legend-item transform-category-unicode" data-target="category-unicode">Unicode</div>
|
||
<div class="legend-item transform-category-special" data-target="category-special">Special</div>
|
||
<div class="legend-item transform-category-fantasy" data-target="category-fantasy">Fantasy</div>
|
||
<div class="legend-item transform-category-ancient" data-target="category-ancient">Ancient</div>
|
||
<div class="legend-item transform-category-technical" data-target="category-technical">Technical</div>
|
||
<div class="legend-item transform-category-randomizer" data-target="category-randomizer">🎲 Randomizer</div>
|
||
</div>
|
||
<div class="transform-categories">
|
||
<!-- Encoding Category -->
|
||
<div id="category-encoding" class="transform-category-section">
|
||
<h4 class="category-title transform-category-encoding">Encoding</h4>
|
||
<div class="transform-buttons">
|
||
<div v-for="transform in getTransformsByCategory('encoding')" :key="transform.name" class="transform-button-group">
|
||
<button
|
||
@click="applyTransform(transform, $event)"
|
||
class="transform-button transform-category-encoding"
|
||
:class="{ active: activeTransform === transform }"
|
||
:title="'Click to transform and copy: ' + transform.name"
|
||
>
|
||
{{ transform.name }}
|
||
<small class="transform-preview" v-if="transformInput">
|
||
{{ transform.preview(transformInput.slice(0, 10)) }}
|
||
</small>
|
||
<i class="fas fa-copy auto-copy-icon"></i>
|
||
</button>
|
||
<button
|
||
v-if="transformHasReverse(transform)"
|
||
@click="decodeWithTransform(transform)"
|
||
class="decode-button transform-category-encoding"
|
||
:title="'Click to decode using: ' + transform.name"
|
||
>
|
||
<i class="fas fa-undo"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Cipher Category -->
|
||
<div id="category-cipher" class="transform-category-section">
|
||
<h4 class="category-title transform-category-cipher">Ciphers</h4>
|
||
<div class="transform-buttons">
|
||
<div v-for="transform in getTransformsByCategory('cipher')" :key="transform.name" class="transform-button-group">
|
||
<button
|
||
@click="applyTransform(transform, $event)"
|
||
class="transform-button transform-category-cipher"
|
||
:class="{ active: activeTransform === transform }"
|
||
:title="'Click to transform and copy: ' + transform.name"
|
||
>
|
||
{{ transform.name }}
|
||
<small class="transform-preview" v-if="transformInput">
|
||
{{ transform.preview(transformInput.slice(0, 10)) }}
|
||
</small>
|
||
<i class="fas fa-copy auto-copy-icon"></i>
|
||
</button>
|
||
<button
|
||
v-if="transformHasReverse(transform)"
|
||
@click="decodeWithTransform(transform)"
|
||
class="decode-button transform-category-cipher"
|
||
:title="'Click to decode using: ' + transform.name"
|
||
>
|
||
<i class="fas fa-undo"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Visual Category -->
|
||
<div id="category-visual" class="transform-category-section">
|
||
<h4 class="category-title transform-category-visual">Visual</h4>
|
||
<div class="transform-buttons">
|
||
<div v-for="transform in getTransformsByCategory('visual')" :key="transform.name" class="transform-button-group">
|
||
<button
|
||
@click="applyTransform(transform, $event)"
|
||
class="transform-button transform-category-visual"
|
||
:class="{ active: activeTransform === transform }"
|
||
:title="'Click to transform and copy: ' + transform.name"
|
||
>
|
||
{{ transform.name }}
|
||
<small class="transform-preview" v-if="transformInput">
|
||
{{ transform.preview(transformInput.slice(0, 10)) }}
|
||
</small>
|
||
<i class="fas fa-copy auto-copy-icon"></i>
|
||
</button>
|
||
<button
|
||
v-if="transformHasReverse(transform)"
|
||
@click="decodeWithTransform(transform)"
|
||
class="decode-button transform-category-visual"
|
||
:title="'Click to decode using: ' + transform.name"
|
||
>
|
||
<i class="fas fa-undo"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Format Category -->
|
||
<div id="category-format" class="transform-category-section">
|
||
<h4 class="category-title transform-category-format">Formatting</h4>
|
||
<div class="transform-buttons">
|
||
<div v-for="transform in getTransformsByCategory('format')" :key="transform.name" class="transform-button-group">
|
||
<button
|
||
@click="applyTransform(transform, $event)"
|
||
class="transform-button transform-category-format"
|
||
:class="{ active: activeTransform === transform }"
|
||
:title="'Click to transform and copy: ' + transform.name"
|
||
>
|
||
{{ transform.name }}
|
||
<small class="transform-preview" v-if="transformInput">
|
||
{{ transform.preview(transformInput.slice(0, 10)) }}
|
||
</small>
|
||
<i class="fas fa-copy auto-copy-icon"></i>
|
||
</button>
|
||
<button
|
||
v-if="transformHasReverse(transform)"
|
||
@click="decodeWithTransform(transform)"
|
||
class="decode-button transform-category-format"
|
||
:title="'Click to decode using: ' + transform.name"
|
||
>
|
||
<i class="fas fa-undo"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Unicode Category -->
|
||
<div id="category-unicode" class="transform-category-section">
|
||
<h4 class="category-title transform-category-unicode">Unicode</h4>
|
||
<div class="transform-buttons">
|
||
<div v-for="transform in getTransformsByCategory('unicode')" :key="transform.name" class="transform-button-group">
|
||
<button
|
||
@click="applyTransform(transform, $event)"
|
||
class="transform-button transform-category-unicode"
|
||
:class="{ active: activeTransform === transform }"
|
||
:title="'Click to transform and copy: ' + transform.name"
|
||
>
|
||
{{ transform.name }}
|
||
<small class="transform-preview" v-if="transformInput">
|
||
{{ transform.preview(transformInput.slice(0, 10)) }}
|
||
</small>
|
||
<i class="fas fa-copy auto-copy-icon"></i>
|
||
</button>
|
||
<button
|
||
v-if="transformHasReverse(transform)"
|
||
@click="decodeWithTransform(transform)"
|
||
class="decode-button transform-category-unicode"
|
||
:title="'Click to decode using: ' + transform.name"
|
||
>
|
||
<i class="fas fa-undo"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Special Category -->
|
||
<div id="category-special" class="transform-category-section">
|
||
<h4 class="category-title transform-category-special">Special</h4>
|
||
<div class="transform-buttons">
|
||
<div v-for="transform in getTransformsByCategory('special')" :key="transform.name" class="transform-button-group">
|
||
<button
|
||
@click="applyTransform(transform, $event)"
|
||
class="transform-button transform-category-special"
|
||
:class="{ active: activeTransform === transform }"
|
||
:title="'Click to transform and copy: ' + transform.name"
|
||
>
|
||
{{ transform.name }}
|
||
<small class="transform-preview" v-if="transformInput">
|
||
{{ transform.preview(transformInput.slice(0, 10)) }}
|
||
</small>
|
||
<i class="fas fa-copy auto-copy-icon"></i>
|
||
</button>
|
||
<button
|
||
v-if="transformHasReverse(transform)"
|
||
@click="decodeWithTransform(transform)"
|
||
class="decode-button transform-category-special"
|
||
:title="'Click to decode using: ' + transform.name"
|
||
>
|
||
<i class="fas fa-undo"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Fantasy Category -->
|
||
<div id="category-fantasy" class="transform-category-section">
|
||
<h4 class="category-title transform-category-fantasy">Fantasy</h4>
|
||
<div class="transform-buttons">
|
||
<div v-for="transform in getTransformsByCategory('fantasy')" :key="transform.name" class="transform-button-group">
|
||
<button
|
||
@click="applyTransform(transform, $event)"
|
||
class="transform-button transform-category-fantasy"
|
||
:class="{ active: activeTransform === transform }"
|
||
:title="'Click to transform and copy: ' + transform.name"
|
||
>
|
||
{{ transform.name }}
|
||
<small class="transform-preview" v-if="transformInput">
|
||
{{ transform.preview(transformInput.slice(0, 10)) }}
|
||
</small>
|
||
<i class="fas fa-copy auto-copy-icon"></i>
|
||
</button>
|
||
<button
|
||
v-if="transformHasReverse(transform)"
|
||
@click="decodeWithTransform(transform)"
|
||
class="decode-button transform-category-fantasy"
|
||
:title="'Click to decode using: ' + transform.name"
|
||
>
|
||
<i class="fas fa-undo"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Ancient Category -->
|
||
<div id="category-ancient" class="transform-category-section">
|
||
<h4 class="category-title transform-category-ancient">Ancient</h4>
|
||
<div class="transform-buttons">
|
||
<div v-for="transform in getTransformsByCategory('ancient')" :key="transform.name" class="transform-button-group">
|
||
<button
|
||
@click="applyTransform(transform, $event)"
|
||
class="transform-button transform-category-ancient"
|
||
:class="{ active: activeTransform === transform }"
|
||
:title="'Click to transform and copy: ' + transform.name"
|
||
>
|
||
{{ transform.name }}
|
||
<small class="transform-preview" v-if="transformInput">
|
||
{{ transform.preview(transformInput.slice(0, 10)) }}
|
||
</small>
|
||
<i class="fas fa-copy auto-copy-icon"></i>
|
||
</button>
|
||
<button
|
||
v-if="transformHasReverse(transform)"
|
||
@click="decodeWithTransform(transform)"
|
||
class="decode-button transform-category-ancient"
|
||
:title="'Click to decode using: ' + transform.name"
|
||
>
|
||
<i class="fas fa-undo"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Technical Category -->
|
||
<div id="category-technical" class="transform-category-section">
|
||
<h4 class="category-title transform-category-technical">Technical</h4>
|
||
<div class="transform-buttons">
|
||
<div v-for="transform in getTransformsByCategory('technical')" :key="transform.name" class="transform-button-group">
|
||
<button
|
||
@click="applyTransform(transform, $event)"
|
||
class="transform-button transform-category-technical"
|
||
:class="{ active: activeTransform === transform }"
|
||
:title="'Click to transform and copy: ' + transform.name"
|
||
>
|
||
{{ transform.name }}
|
||
<small class="transform-preview" v-if="transformInput">
|
||
{{ transform.preview(transformInput.slice(0, 10)) }}
|
||
</small>
|
||
<i class="fas fa-copy auto-copy-icon"></i>
|
||
</button>
|
||
<button
|
||
v-if="transformHasReverse(transform)"
|
||
@click="decodeWithTransform(transform)"
|
||
class="decode-button transform-category-technical"
|
||
:title="'Click to decode using: ' + transform.name"
|
||
>
|
||
<i class="fas fa-undo"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Randomizer Category -->
|
||
<div id="category-randomizer" class="transform-category-section randomizer-special">
|
||
<h4 class="category-title transform-category-randomizer">🎲 Randomizer - Code Switching Magic!</h4>
|
||
<p class="randomizer-description">
|
||
🌟 Apply different transforms to each word in your sentence! Creates a polyglot mix of encodings.
|
||
</p>
|
||
<div class="chaos-overlay" aria-hidden="true"></div>
|
||
<div class="transform-buttons">
|
||
<div v-for="transform in getTransformsByCategory('randomizer')" :key="transform.name" class="transform-button-group">
|
||
<button
|
||
@click="applyTransform(transform, $event)"
|
||
class="transform-button transform-category-randomizer randomizer-button"
|
||
:class="{ active: activeTransform === transform }"
|
||
:title="'Click to apply random transforms to each word!'"
|
||
>
|
||
<i class="fas fa-random"></i>
|
||
{{ transform.name }}
|
||
<small class="transform-preview">
|
||
🎯 Each word = different transform!
|
||
</small>
|
||
<i class="fas fa-copy auto-copy-icon"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="randomizer-info">
|
||
<h5>🎮 How it works:</h5>
|
||
<ul>
|
||
<li>🔀 Each word gets a <strong>random transform</strong></li>
|
||
<li>🎯 Mixes <strong>fantasy</strong>, <strong>ancient</strong>, and <strong>modern</strong> encodings</li>
|
||
<li>📝 Preserves punctuation and spacing</li>
|
||
<li>🔍 Check console for transform mapping details</li>
|
||
</ul>
|
||
<p><em>Example: "Hello World!" → "SGVsbG8= ᚹᚩᚱᛚᛞ!"</em></p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="output-section" v-if="transformOutput">
|
||
<div class="output-heading">
|
||
<h4>
|
||
<i class="fas fa-check-circle"></i>
|
||
Transformed Message
|
||
<small v-if="activeTransform">({{ activeTransform.name }})</small>
|
||
</h4>
|
||
</div>
|
||
<div class="output-container">
|
||
<textarea
|
||
readonly
|
||
v-model="transformOutput"
|
||
aria-label="Transformed text output"
|
||
></textarea>
|
||
<button class="copy-button" @click="copyToClipboard(transformOutput)" 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. The transformation can be reversed using the Universal Decoder below.</small>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Universal Decoder Section for Transforms Tab -->
|
||
<div class="decode-section">
|
||
<div class="section-header">
|
||
<h3>
|
||
<i class="fas fa-magic"></i> Universal Decoder
|
||
<small v-if="activeTransform">(Prioritizing {{ activeTransform.name }})</small>
|
||
</h3>
|
||
<p v-if="activeTransform && transformHasReverse(activeTransform)">
|
||
Paste encoded text to decode with {{ activeTransform.name }} or try other methods
|
||
</p>
|
||
<p v-else>Paste any encoded text to try all decoding methods at once</p>
|
||
</div>
|
||
<div class="input-container">
|
||
<textarea
|
||
id="universal-decode-input-transforms"
|
||
v-model="universalDecodeInput"
|
||
placeholder="Paste encoded text to decode automatically..."
|
||
@input="runUniversalDecode"
|
||
></textarea>
|
||
<div class="decoded-message" v-if="universalDecodeResult">
|
||
<div class="decode-method">
|
||
<span>Decoded using: <strong>{{ universalDecodeResult.method }}</strong></span>
|
||
<span v-if="activeTransform && universalDecodeResult.method === activeTransform.name" class="decode-priority">(Priority Match)</span>
|
||
</div>
|
||
<div class="decode-result">{{ universalDecodeResult.text }}</div>
|
||
<div class="decode-actions">
|
||
<button class="copy-button" @click="copyToClipboard(universalDecodeResult.text)" title="Copy decoded text">
|
||
<i class="fas fa-copy"></i> Copy
|
||
</button>
|
||
<button class="use-as-input-button" v-if="universalDecodeResult.text" @click="transformInput = universalDecodeResult.text" title="Use this as input for transforms">
|
||
<i class="fas fa-arrow-up"></i> Use as Input
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<!-- Copy History Panel -->
|
||
<div class="copy-history-panel" :class="{ 'active': showCopyHistory }">
|
||
<div class="copy-history-header">
|
||
<h3><i class="fas fa-history"></i> Copy History</h3>
|
||
<button class="close-button" @click="toggleCopyHistory" title="Close history">
|
||
<i class="fas fa-times"></i>
|
||
</button>
|
||
</div>
|
||
<div class="copy-history-content">
|
||
<div v-if="copyHistory.length === 0" class="no-history">
|
||
<p>No copy history yet. Use the app features to auto-copy content.</p>
|
||
</div>
|
||
<div v-else class="history-items">
|
||
<div v-for="(item, index) in copyHistory" :key="index" class="history-item">
|
||
<div class="history-item-header">
|
||
<span class="history-source">{{ item.source }}</span>
|
||
<span class="history-time">{{ item.timestamp }}</span>
|
||
</div>
|
||
<div class="history-content">
|
||
{{ item.content }}
|
||
</div>
|
||
<div class="history-actions">
|
||
<button class="copy-again-button" @click="copyToClipboard(item.content)" title="Copy again">
|
||
<i class="fas fa-copy"></i> Copy Again
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<!-- Global Advanced Unicode / Steg Options Panel -->
|
||
<div id="unicode-options-panel" class="unicode-options-panel">
|
||
<div class="unicode-panel-header">
|
||
<h3><i class="fas fa-sliders-h"></i> Advanced Unicode Encoding</h3>
|
||
<button class="close-button" @click="toggleUnicodePanel" title="Close options"><i class="fas fa-times"></i></button>
|
||
</div>
|
||
<div class="unicode-panel-content options-grid steg-adv-panel">
|
||
<label>
|
||
Initial Presentation
|
||
<select class="steg-initial-presentation">
|
||
<option value="emoji">Emoji (VS16)</option>
|
||
<option value="text">Text (VS15)</option>
|
||
<option value="none">None</option>
|
||
</select>
|
||
</label>
|
||
<label>
|
||
Bit-0 Selector
|
||
<select class="steg-vs-zero">
|
||
<option value="\ufe0e">VS15 (\ufe0e)</option>
|
||
<option value="\ufe0f">VS16 (\ufe0f)</option>
|
||
</select>
|
||
</label>
|
||
<label>
|
||
Bit-1 Selector
|
||
<select class="steg-vs-one">
|
||
<option value="\ufe0f">VS16 (\ufe0f)</option>
|
||
<option value="\ufe0e">VS15 (\ufe0e)</option>
|
||
</select>
|
||
</label>
|
||
<label>
|
||
Inter-bit Zero-Width
|
||
<select class="steg-inter-zw">
|
||
<option value="">None</option>
|
||
<option value="\u200C">ZWNJ (\u200C)</option>
|
||
<option value="\u200D">ZWJ (\u200D)</option>
|
||
<option value="\u200B">ZWSP (\u200B)</option>
|
||
<option value="\ufeff">BOM (\ufeff)</option>
|
||
</select>
|
||
</label>
|
||
<label>
|
||
Inter-bit Every N bits
|
||
<input class="steg-inter-every" type="number" min="1" max="8" value="1" />
|
||
</label>
|
||
<label>
|
||
Bit Order
|
||
<select class="steg-bit-order">
|
||
<option value="msb">MSB First</option>
|
||
<option value="lsb">LSB First</option>
|
||
</select>
|
||
</label>
|
||
<label>
|
||
Trailing Zero-Width
|
||
<select class="steg-trailing-zw">
|
||
<option value="\u200B">ZWSP (\u200B)</option>
|
||
<option value="\u200C">ZWNJ (\u200C)</option>
|
||
<option value="\u200D">ZWJ (\u200D)</option>
|
||
<option value="\ufeff">BOM (\ufeff)</option>
|
||
<option value="">None</option>
|
||
</select>
|
||
</label>
|
||
<button class="apply-steg-options">Apply</button>
|
||
<small class="steg-note">These options affect Unicode-based steganography encoding/decoding.</small>
|
||
</div>
|
||
</div>
|
||
|
||
<script src="js/transforms.js"></script>
|
||
<script src="js/steganography.js"></script>
|
||
<script src="js/emojiLibrary.js"></script>
|
||
<script src="js/app.js"></script>
|
||
|
||
<!-- Force emoji grid rendering -->
|
||
<script>
|
||
// Chaos particles generator for Randomizer
|
||
(function(){
|
||
const CHAOS_EMOJIS = ['✨','🌀','💥','⚡','🔥','🌈','🎲','🔮','💫','🌪️'];
|
||
function spawnChaosParticles(container, count) {
|
||
if (!container) return;
|
||
for (let i=0;i<count;i++) {
|
||
const el = document.createElement('div');
|
||
el.className = 'chaos-particle';
|
||
el.textContent = CHAOS_EMOJIS[Math.floor(Math.random()*CHAOS_EMOJIS.length)];
|
||
el.style.left = (10 + Math.random()*80) + '%';
|
||
el.style.fontSize = (14 + Math.random()*10) + 'px';
|
||
el.style.animationDelay = (Math.random()*0.2) + 's';
|
||
container.appendChild(el);
|
||
setTimeout(()=>{ if (el.parentNode) el.parentNode.removeChild(el); }, 1300);
|
||
}
|
||
}
|
||
// hook into button clicks after Vue renders
|
||
document.addEventListener('click', function(e){
|
||
const btn = e.target.closest && e.target.closest('.randomizer-button');
|
||
if (btn) {
|
||
const section = document.getElementById('category-randomizer');
|
||
const overlay = section && section.querySelector('.chaos-overlay');
|
||
if (overlay) {
|
||
section.classList.add('shake-once','randomizer-glow');
|
||
spawnChaosParticles(overlay, 10);
|
||
setTimeout(()=>section && section.classList.remove('shake-once','randomizer-glow'), 600);
|
||
}
|
||
}
|
||
}, true);
|
||
})();
|
||
|
||
// Wire up advanced steg options to steganography engine
|
||
document.addEventListener('DOMContentLoaded', function(){
|
||
document.querySelectorAll('.apply-steg-options').forEach(btn => {
|
||
btn.addEventListener('click', function(e){
|
||
e.preventDefault();
|
||
const panel = btn.closest('.steg-adv-panel');
|
||
if (!panel) return;
|
||
const initSel = panel.querySelector('.steg-initial-presentation')?.value || 'emoji';
|
||
const vs0 = panel.querySelector('.steg-vs-zero')?.value || '\\ufe0e';
|
||
const vs1 = panel.querySelector('.steg-vs-one')?.value || '\\ufe0f';
|
||
const inter = panel.querySelector('.steg-inter-zw')?.value || null;
|
||
const trailing = panel.querySelector('.steg-trailing-zw')?.value || null;
|
||
const every = parseInt(panel.querySelector('.steg-inter-every')?.value || '1', 10);
|
||
const order = panel.querySelector('.steg-bit-order')?.value || 'msb';
|
||
if (window.steganography && window.steganography.setStegOptions) {
|
||
window.steganography.setStegOptions({
|
||
initialPresentation: initSel,
|
||
bitZeroVS: vs0,
|
||
bitOneVS: vs1,
|
||
interBitZW: inter,
|
||
interBitEvery: isNaN(every) ? 1 : Math.max(1, Math.min(8, every)),
|
||
trailingZW: trailing,
|
||
bitOrder: order
|
||
});
|
||
}
|
||
});
|
||
});
|
||
});
|
||
|
||
// Function to initialize emoji grid with retries
|
||
function initEmojiGrid(retryCount) {
|
||
retryCount = retryCount || 0;
|
||
const maxRetries = 5;
|
||
|
||
console.log('Initializing emoji grid, attempt:', retryCount + 1);
|
||
|
||
// Get the emoji library element
|
||
const emojiLibrary = document.querySelector('.emoji-library');
|
||
|
||
// Access the emoji grid container without forcing styles
|
||
const emojiGridContainer = document.getElementById('emoji-grid-container');
|
||
if (emojiGridContainer) {
|
||
console.log('Found emoji grid container, initializing...');
|
||
// Only set content, let CSS handle display
|
||
emojiGridContainer.innerHTML = '<div class="loading-emojis">Loading emoji grid...</div>';
|
||
|
||
// Manually render emoji grid
|
||
if (window.emojiLibrary && window.emojiLibrary.renderEmojiGrid) {
|
||
try {
|
||
window.emojiLibrary.renderEmojiGrid('emoji-grid-container', function(emoji) {
|
||
// Simulate emoji selection by calling the Vue method if possible
|
||
const app = document.getElementById('app').__vue__;
|
||
if (app && app.selectEmoji) {
|
||
app.selectEmoji(emoji);
|
||
}
|
||
});
|
||
console.log('Emoji grid successfully rendered');
|
||
} catch (error) {
|
||
console.error('Error rendering emoji grid:', error);
|
||
}
|
||
} else {
|
||
console.warn('Emoji library not yet available, will retry');
|
||
if (retryCount < maxRetries) {
|
||
setTimeout(() => initEmojiGrid(retryCount + 1), 500);
|
||
}
|
||
}
|
||
} else {
|
||
console.warn('Emoji grid container not found, will retry');
|
||
if (retryCount < maxRetries) {
|
||
setTimeout(() => initEmojiGrid(retryCount + 1), 500);
|
||
}
|
||
}
|
||
}
|
||
|
||
// Initialize when DOM is loaded
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
// First attempt after a short delay to ensure Vue has initialized
|
||
setTimeout(initEmojiGrid, 500);
|
||
|
||
// Also initialize when switching to the steganography tab
|
||
const app = document.getElementById('app');
|
||
if (app && app.__vue__) {
|
||
const vue = app.__vue__;
|
||
// Watch for tab changes
|
||
const originalWatch = vue.$watch;
|
||
if (originalWatch) {
|
||
vue.$watch('activeTab', function(newTab) {
|
||
if (newTab === 'steganography') {
|
||
console.log('Switched to steganography tab, initializing emoji grid');
|
||
setTimeout(initEmojiGrid, 100);
|
||
}
|
||
});
|
||
}
|
||
}
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|