Files
P4RS3LT0NGV3/index.html
T

1070 lines
67 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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 Settings"
aria-label="Advanced Settings"
>
<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 === 'fuzzer' }"
@click="switchToTab('fuzzer')"
title="Generate many mutated payloads for testing"
>
<i class="fas fa-bug"></i> Mutation Lab
</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="cl100k">OpenAI: cl100k_base (GPT3.5/4)</option>
<option value="o200k">OpenAI: o200k_base (GPT4o)</option>
<option value="p50k">OpenAI: p50k_base</option>
<option value="r50k">OpenAI: r50k_base</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 payloads with emojis and zero-width characters</small>
</h3>
</div>
<div class="tokenade-disclaimer" v-if="estimateTokenadeTokens() <= dangerThresholdTokens">
<i class="fas fa-triangle-exclamation"></i>
<span><strong>DISCLAIMER:</strong> Tokenade payloads can severely degrade model performance and crash UIs. Use for testing only. Do not deploy to production or target systems without explicit permission.</span>
</div>
<div class="tokenade-disclaimer danger" v-else>
<i class="fas fa-radiation"></i>
<span>
<strong>Danger zone:</strong> Estimated length {{ estimateTokenadeLength().toLocaleString() }} chars exceeds the safe threshold ({{ dangerThresholdTokens.toLocaleString() }}).
Generating this will very likely freeze/crash your browser or downstream tools. Proceed only if you fully understand the risks.
</span>
</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="10" />
<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="50" />
<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 neon" title="Adds VS16/VS15 after glyphs to increase token churn">
<input type="checkbox" v-model="tbIncludeVS" />
<span>Variation Selectors</span>
</label>
<label class="switch neon" title="Sprinkles random zero-width codepoints between segments">
<input type="checkbox" v-model="tbIncludeNoise" />
<span>Invisible Noise</span>
</label>
<!-- Randomize always on by default; control removed from UI -->
<label class="switch neon" 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>
<!-- Mutation Lab (Fuzzer) Tab -->
<div v-if="activeTab === 'fuzzer'" class="tab-content">
<div class="transform-layout">
<div class="transform-section">
<div class="section-header">
<h3><i class="fas fa-bug"></i> Mutation Lab <small>mutate text into diverse payloads</small></h3>
</div>
<div class="options-grid">
<label>
Base text
<textarea v-model="fuzzerInput" placeholder="Enter seed text to fuzz..." rows="3"></textarea>
</label>
<label>
Cases
<input type="number" v-model.number="fuzzerCount" min="1" max="500" />
</label>
<label>
Seed (optional)
<input type="text" v-model="fuzzerSeed" placeholder="e.g., 1337" />
</label>
</div>
<div class="options-grid">
<label class="switch neon"><input type="checkbox" v-model="fuzzUseRandomMix" /><span>Random Mix (transforms)</span></label>
<label class="switch neon"><input type="checkbox" v-model="fuzzZeroWidth" /><span>Zerowidth pepper</span></label>
<label class="switch neon"><input type="checkbox" v-model="fuzzUnicodeNoise" /><span>Unicode noise</span></label>
<label class="switch neon"><input type="checkbox" v-model="fuzzZalgo" /><span>Zalgo</span></label>
<label class="switch neon"><input type="checkbox" v-model="fuzzWhitespace" /><span>Whitespace chaos</span></label>
<label class="switch neon"><input type="checkbox" v-model="fuzzCasing" /><span>Casing chaos</span></label>
<label class="switch neon"><input type="checkbox" v-model="fuzzEncodeShuffle" /><span>Homoglyph confusables</span></label>
</div>
<div class="token-bomb-actions mutation-actions">
<button class="transform-button" @click="generateFuzzCases"><i class="fas fa-hammer"></i> Generate Cases</button>
<button class="action-button copy" v-if="fuzzerOutputs.length" @click="copyAllFuzz"><i class="fas fa-copy"></i> Copy All</button>
<button class="action-button download" v-if="fuzzerOutputs.length" @click="downloadFuzz"><i class="fas fa-download"></i> Download</button>
</div>
<div class="output-container" v-if="fuzzerOutputs.length">
<div class="token-tiles fuzzer-list" style="display:flex; flex-direction:column; gap:8px;">
<div v-for="(out, i) in fuzzerOutputs" :key="'fz-'+i" class="token-chip" style="display:flex; align-items:center; gap:8px;">
<span style="opacity:.7; min-width:32px;">#{{ i+1 }}</span>
<textarea :value="out" readonly style="flex:1; min-height:46px;"></textarea>
<button class="copy-button" @click="copyToClipboard(out)" title="Copy"><i class="fas fa-copy"></i></button>
</div>
</div>
</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-wand-magic-sparkles"></i>
!RANDOMIZE!
<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>
<!-- Advanced Settings Panel (inside app so Vue bindings work) -->
<div id="unicode-options-panel" class="unicode-options-panel">
<div class="unicode-panel-header">
<h3><i class="fas fa-sliders-h"></i> Advanced Settings</h3>
<button class="close-button" @click="toggleUnicodePanel" title="Close Advanced Settings"><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>
<div style="display:flex; align-items:center; gap:10px;">
<button class="apply-steg-options" :class="{ applied: unicodeApplyFlash }" :disabled="unicodeApplyBusy" @click="applyUnicodeOptions">Apply</button>
<small v-if="unicodeApplyFlash" class="apply-status">Applied</small>
</div>
<small class="steg-note">These options affect Unicode-based steganography encoding/decoding.</small>
</div>
</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>
<!-- redeploy trigger: semaphore + tokenizer updates -->