@whykusanagi/corrupted-theme 0.1.7 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,348 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>NSFW Corruption (18+) - Corrupted Theme</title>
7
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css">
8
- <link rel="stylesheet" href="../../src/css/theme.css">
9
- <style>
10
- .container {
11
- max-width: 1000px;
12
- margin: 0 auto;
13
- padding: var(--spacing-lg);
14
- }
15
-
16
- .example-group {
17
- margin-bottom: var(--spacing-2xl);
18
- background: var(--glass);
19
- border: 1px solid var(--border);
20
- border-radius: var(--radius-lg);
21
- padding: var(--spacing-lg);
22
- }
23
-
24
- .example-group h3 {
25
- color: var(--accent);
26
- margin-bottom: var(--spacing-lg);
27
- font-size: 1.25rem;
28
- }
29
-
30
- .warning-box {
31
- background: rgba(255, 0, 0, 0.1);
32
- border: 3px solid var(--corrupted-red);
33
- border-radius: var(--radius-lg);
34
- padding: var(--spacing-xl);
35
- margin-bottom: var(--spacing-2xl);
36
- box-shadow: 0 0 30px rgba(255, 0, 0, 0.3);
37
- }
38
-
39
- .warning-box h2 {
40
- color: var(--corrupted-red);
41
- margin-top: 0;
42
- font-size: 1.75rem;
43
- text-align: center;
44
- }
45
-
46
- .warning-box p {
47
- color: var(--text);
48
- line-height: 1.6;
49
- }
50
-
51
- .warning-box ul {
52
- color: var(--text-secondary);
53
- line-height: 1.8;
54
- }
55
-
56
- .demo-area {
57
- background: var(--bg-secondary);
58
- border: 1px solid var(--border);
59
- border-radius: var(--radius-md);
60
- padding: var(--spacing-xl);
61
- margin-bottom: var(--spacing-md);
62
- min-height: 100px;
63
- display: flex;
64
- align-items: center;
65
- justify-content: center;
66
- }
67
-
68
- .typing-output {
69
- font-size: 1.25rem;
70
- min-height: 40px;
71
- letter-spacing: 1px;
72
- font-family: 'Courier New', monospace;
73
- text-align: center;
74
- width: 100%;
75
- }
76
-
77
- .controls {
78
- display: flex;
79
- gap: var(--spacing-md);
80
- margin-top: var(--spacing-md);
81
- flex-wrap: wrap;
82
- }
83
-
84
- .code-block {
85
- background: var(--bg);
86
- border: 1px solid var(--border);
87
- border-radius: var(--radius-md);
88
- padding: var(--spacing-md);
89
- margin-top: var(--spacing-md);
90
- font-family: 'Courier New', monospace;
91
- font-size: 0.875rem;
92
- color: var(--text-secondary);
93
- overflow-x: auto;
94
- }
95
-
96
- .info-badge {
97
- display: inline-flex;
98
- align-items: center;
99
- gap: var(--spacing-sm);
100
- background: var(--glass);
101
- border: 1px solid var(--border);
102
- border-radius: var(--radius-full);
103
- padding: var(--spacing-sm) var(--spacing-md);
104
- font-size: 0.875rem;
105
- color: var(--text-secondary);
106
- margin-bottom: var(--spacing-md);
107
- }
108
-
109
- .nsfw-badge {
110
- background: rgba(255, 0, 0, 0.2);
111
- border-color: var(--corrupted-red);
112
- color: var(--corrupted-red);
113
- }
114
-
115
- .comparison-grid {
116
- display: grid;
117
- grid-template-columns: 1fr 1fr;
118
- gap: var(--spacing-lg);
119
- margin-bottom: var(--spacing-md);
120
- }
121
-
122
- .comparison-box {
123
- background: var(--bg-secondary);
124
- border: 2px solid;
125
- border-radius: var(--radius-md);
126
- padding: var(--spacing-lg);
127
- }
128
-
129
- .comparison-box.sfw {
130
- border-color: var(--corrupted-magenta2);
131
- }
132
-
133
- .comparison-box.nsfw {
134
- border-color: var(--corrupted-purple);
135
- }
136
-
137
- .comparison-box h4 {
138
- margin-top: 0;
139
- font-size: 1rem;
140
- }
141
-
142
- .comparison-box.sfw h4 {
143
- color: var(--corrupted-magenta2);
144
- }
145
-
146
- .comparison-box.nsfw h4 {
147
- color: var(--corrupted-purple);
148
- }
149
-
150
- @media (max-width: 768px) {
151
- .comparison-grid {
152
- grid-template-columns: 1fr;
153
- }
154
- }
155
- </style>
156
- </head>
157
- <body>
158
- <!-- Global Navigation -->
159
- <nav class="navbar">
160
- <div class="navbar-content">
161
- <a href="../index.html" class="navbar-logo"><i class="fas fa-palette"></i> Corrupted Theme</a>
162
- <ul class="navbar-links">
163
- <li><a href="../index.html"><i class="fas fa-home"></i> Home</a></li>
164
- <li><a href="../showcase-complete.html"><i class="fas fa-cube"></i> Components</a></li>
165
- <li><a href="../extensions-showcase.html"><i class="fas fa-puzzle-piece"></i> Extensions</a></li>
166
- <li class="has-submenu">
167
- <a href="#" class="active">
168
- <i class="fas fa-flask"></i> Examples
169
- <i class="fas fa-chevron-down" style="font-size: 0.7em; margin-left: 4px;"></i>
170
- </a>
171
- <div class="submenu">
172
- <a href="../nikke-team-builder.html"><i class="fas fa-users"></i> Nikke Team Builder</a>
173
- <a href="../button.html"><i class="fas fa-hand-pointer"></i> Buttons</a>
174
- <a href="../card.html"><i class="fas fa-square"></i> Cards</a>
175
- <a href="../form.html"><i class="fas fa-edit"></i> Forms</a>
176
- <a href="../layout.html"><i class="fas fa-columns"></i> Layouts</a>
177
- <a href="../basic/corrupted-text.html"><i class="fas fa-terminal"></i> Character Corruption</a>
178
- <a href="../basic/typing-animation.html"><i class="fas fa-keyboard"></i> Buffer Corruption</a>
179
- <a href="nsfw-corruption.html" class="active"><i class="fas fa-exclamation-triangle"></i> NSFW (18+)</a>
180
- </div>
181
- </li>
182
- <li><a href="../showcase-complete.html"><i class="fas fa-book"></i> Docs</a></li>
183
- </ul>
184
- </div>
185
- </nav>
186
-
187
- <div class="container">
188
- <!-- Content Warning -->
189
- <div class="warning-box">
190
- <h2><i class="fas fa-exclamation-triangle"></i> 18+ Content Warning</h2>
191
- <p style="text-align: center;"><strong>This page contains explicit mature content (NSFW mode).</strong></p>
192
- <p>This example demonstrates the NSFW mode of TypingAnimation, which includes:</p>
193
- <ul>
194
- <li>Explicit intimate/sexual phrases</li>
195
- <li>Loss of control themes</li>
196
- <li>Adult-oriented corruption aesthetics</li>
197
- </ul>
198
- <p><strong>NOT suitable for:</strong></p>
199
- <ul>
200
- <li>Professional/corporate projects</li>
201
- <li>Public streams without 18+ rating</li>
202
- <li>Educational contexts</li>
203
- <li>All-ages content</li>
204
- </ul>
205
- <p style="text-align: center; margin-top: var(--spacing-lg); font-weight: bold;">
206
- By continuing, you confirm you are 18+ and accept viewing explicit content.
207
- </p>
208
- </div>
209
-
210
- <h1><i class="fas fa-exclamation-triangle"></i> NSFW Buffer Corruption</h1>
211
- <p style="color: var(--text-secondary); margin-bottom: var(--spacing-lg);">
212
- Pattern 2: Phrase flickering with explicit adult content (18+ only).
213
- </p>
214
-
215
- <div class="info-badge nsfw-badge">
216
- <i class="fas fa-exclamation-triangle"></i>
217
- <span>18+ ONLY - Explicit Content</span>
218
- </div>
219
-
220
- <!-- SFW vs NSFW Comparison -->
221
- <section id="comparison" class="example-group">
222
- <h3>SFW vs NSFW Comparison</h3>
223
- <p>See the difference between default SFW mode and explicit NSFW mode side-by-side.</p>
224
-
225
- <div class="comparison-grid">
226
- <div class="comparison-box sfw">
227
- <h4><i class="fas fa-check-circle"></i> SFW Mode (Default)</h4>
228
- <div class="demo-area">
229
- <div class="typing-output" id="outputSFW"></div>
230
- </div>
231
- </div>
232
- <div class="comparison-box nsfw">
233
- <h4><i class="fas fa-exclamation-triangle"></i> NSFW Mode (Explicit)</h4>
234
- <div class="demo-area">
235
- <div class="typing-output" id="outputNSFW"></div>
236
- </div>
237
- </div>
238
- </div>
239
-
240
- <div class="controls">
241
- <button class="btn" onclick="startComparison()">
242
- <i class="fas fa-play"></i> Start Comparison
243
- </button>
244
- </div>
245
-
246
- <div class="code-block">// SFW mode (default - safe for all audiences)
247
- const typingSFW = new TypingAnimation(element, {
248
- nsfw: false // Default behavior
249
- });
250
-
251
- // NSFW mode (explicit opt-in required)
252
- const typingNSFW = new TypingAnimation(element, {
253
- nsfw: true // ⚠️ Enables 18+ explicit content
254
- });</div>
255
- </section>
256
-
257
- <!-- NSFW-Only Example -->
258
- <section id="nsfw-only" class="example-group">
259
- <h3>NSFW-Only Example</h3>
260
- <p>Pure NSFW buffer corruption with explicit phrases in purple (#8b5cf6).</p>
261
-
262
- <div class="demo-area">
263
- <div class="typing-output" id="outputNSFWOnly"></div>
264
- </div>
265
-
266
- <div class="controls">
267
- <button class="btn" onclick="startNSFWOnly()">
268
- <i class="fas fa-play"></i> Start NSFW Animation
269
- </button>
270
- <button class="btn btn-secondary" onclick="stopNSFWOnly()">
271
- <i class="fas fa-stop"></i> Stop
272
- </button>
273
- </div>
274
-
275
- <div class="code-block">const typing = new TypingAnimation(element, {
276
- nsfw: true, // ⚠️ NSFW mode enabled
277
- typingSpeed: 35,
278
- glitchChance: 0.12
279
- });
280
-
281
- typing.start('Neural corruption detected...');</div>
282
- </section>
283
-
284
- <!-- Component Info -->
285
- <section class="example-group">
286
- <h3><i class="fas fa-info-circle"></i> NSFW Mode Configuration</h3>
287
- <ul style="color: var(--text-secondary); line-height: 1.8;">
288
- <li><strong>Requires Explicit Opt-in:</strong> <code>{ nsfw: true }</code></li>
289
- <li><strong>Phrase Examples:</strong> "壊れちゃう...", "Pleasure protocols loading...", "変態", "えっち"</li>
290
- <li><strong>Color:</strong> Deep Purple (#8b5cf6) for NSFW corruption</li>
291
- <li><strong>Default Behavior:</strong> SFW mode (safe for all audiences)</li>
292
- <li><strong>Content Rating:</strong> 18+ only, explicit intimate content</li>
293
- </ul>
294
-
295
- <div class="alert alert-warning" style="margin-top: var(--spacing-md);">
296
- <i class="fas fa-shield-alt"></i>
297
- <strong>Important:</strong> NSFW phrases are ONLY shown when <code>nsfw: true</code> is explicitly set.
298
- The default behavior is always SFW for safety.
299
- </div>
300
- </section>
301
- </div>
302
-
303
- <script type="module">
304
- import { TypingAnimation } from '../../src/core/typing-animation.js';
305
-
306
- // SFW vs NSFW Comparison
307
- const outputSFW = document.getElementById('outputSFW');
308
- const outputNSFW = document.getElementById('outputNSFW');
309
-
310
- window.startComparison = () => {
311
- // SFW instance
312
- const typingSFW = new TypingAnimation(outputSFW, {
313
- nsfw: false,
314
- glitchChance: 0.10
315
- });
316
-
317
- // NSFW instance
318
- const typingNSFW = new TypingAnimation(outputNSFW, {
319
- nsfw: true,
320
- glitchChance: 0.10
321
- });
322
-
323
- // Start both simultaneously
324
- typingSFW.start('System Online');
325
- typingNSFW.start('System Online');
326
- };
327
-
328
- // NSFW-only example
329
- const outputNSFWOnly = document.getElementById('outputNSFWOnly');
330
- let typingNSFWOnly = new TypingAnimation(outputNSFWOnly, {
331
- nsfw: true,
332
- typingSpeed: 35,
333
- glitchChance: 0.12
334
- });
335
-
336
- window.startNSFWOnly = () => {
337
- typingNSFWOnly.start('Neural corruption detected... Decoding data buffer...');
338
- };
339
-
340
- window.stopNSFWOnly = () => {
341
- typingNSFWOnly.stop();
342
- };
343
-
344
- // Auto-start comparison on load
345
- setTimeout(() => startComparison(), 1000);
346
- </script>
347
- </body>
348
- </html>
@@ -1,300 +0,0 @@
1
- /**
2
- * Corrupted Text Animation
3
- *
4
- * Cycles through Japanese (hiragana/katakana/kanji), romaji, and English text variants
5
- * with character-level corruption effects. The text progressively corrupts from one
6
- * variant to another, creating a glitchy Matrix-style transformation effect.
7
- *
8
- * @class CorruptedText
9
- * @version 1.0.0
10
- * @author whykusanagi
11
- * @license MIT
12
- *
13
- * @example Basic Usage (Auto-initialization via data attributes)
14
- * ```html
15
- * <span class="corrupted-multilang"
16
- * data-english="Hello World"
17
- * data-romaji="konnichiwa"
18
- * data-hiragana="こんにちは"
19
- * data-katakana="コンニチハ"
20
- * data-kanji="今日は">
21
- * </span>
22
- * ```
23
- *
24
- * @example Manual Initialization
25
- * ```javascript
26
- * const element = document.querySelector('.my-text');
27
- * const corrupted = new CorruptedText(element, {
28
- * duration: 3000,
29
- * cycleDelay: 100,
30
- * loop: true
31
- * });
32
- *
33
- * // Control playback
34
- * corrupted.start();
35
- * corrupted.stop();
36
- * corrupted.restart();
37
- * corrupted.settle('Final Text');
38
- * ```
39
- *
40
- * @see https://github.com/whykusanagi/corrupted-theme
41
- * @see CORRUPTED_THEME_SPEC.md - Character-by-Character Decoding pattern
42
- */
43
- class CorruptedText {
44
- /**
45
- * Creates a new CorruptedText animation instance
46
- *
47
- * @param {HTMLElement} element - The DOM element to animate
48
- * @param {Object} [options={}] - Configuration options
49
- * @param {number} [options.duration=3000] - Total animation duration in milliseconds
50
- * @param {number} [options.cycleDelay=100] - Delay between character corruption steps (ms)
51
- * @param {number} [options.startDelay=0] - Initial delay before animation starts (ms)
52
- * @param {boolean} [options.loop=true] - Whether to loop through variants continuously
53
- * @param {string|null} [options.finalText=null] - Text to settle on after cycle (if loop=false)
54
- */
55
- constructor(element, options = {}) {
56
- this.element = element;
57
- this.options = {
58
- duration: options.duration || 3000,
59
- cycleDelay: options.cycleDelay || 100,
60
- startDelay: options.startDelay || 0,
61
- loop: options.loop !== false,
62
- finalText: options.finalText || null,
63
- ...options
64
- };
65
-
66
- // Get text variants from data attributes
67
- this.variants = {
68
- english: this.element.dataset.english || this.element.textContent.trim(),
69
- romaji: this.element.dataset.romaji || null,
70
- hiragana: this.element.dataset.hiragana || null,
71
- katakana: this.element.dataset.katakana || null,
72
- kanji: this.element.dataset.kanji || null
73
- };
74
-
75
- // Filter out null variants and create array of available variants
76
- this.availableVariants = Object.entries(this.variants)
77
- .filter(([_, value]) => value !== null)
78
- .map(([key, value]) => ({ type: key, text: value }));
79
-
80
- if (this.availableVariants.length === 0) {
81
- console.warn('CorruptedText: No text variants found for element', element);
82
- return;
83
- }
84
-
85
- this.currentVariantIndex = 0;
86
- this.isAnimating = false;
87
- this.animationFrame = null;
88
- this._startDelayId = null;
89
- this._animateTimeoutId = null;
90
- this._corruptTimeoutId = null;
91
-
92
- this.init();
93
- }
94
-
95
- /**
96
- * Initialize the corruption animation
97
- * @private
98
- */
99
- init() {
100
- // Add corrupted class for styling
101
- if (!this.element.classList.contains('corrupted-multilang')) {
102
- this.element.classList.add('corrupted-multilang');
103
- }
104
-
105
- // Store original text
106
- this.originalText = this.element.textContent.trim();
107
-
108
- // Start animation after configured delay
109
- if (this.options.startDelay > 0) {
110
- this._startDelayId = setTimeout(() => this.start(), this.options.startDelay);
111
- } else {
112
- this.start();
113
- }
114
- }
115
-
116
- /**
117
- * Start the corruption animation
118
- * @public
119
- */
120
- start() {
121
- if (this.isAnimating) return;
122
- this.isAnimating = true;
123
- this.animate();
124
- }
125
-
126
- /**
127
- * Stop the corruption animation
128
- * @public
129
- */
130
- stop() {
131
- this.isAnimating = false;
132
- if (this.animationFrame) cancelAnimationFrame(this.animationFrame);
133
- if (this._startDelayId) clearTimeout(this._startDelayId);
134
- if (this._animateTimeoutId) clearTimeout(this._animateTimeoutId);
135
- if (this._corruptTimeoutId) clearTimeout(this._corruptTimeoutId);
136
- this.animationFrame = null;
137
- this._startDelayId = null;
138
- this._animateTimeoutId = null;
139
- this._corruptTimeoutId = null;
140
- }
141
-
142
- /**
143
- * Fully tear down this instance: stop animation and release element reference.
144
- * @public
145
- */
146
- destroy() {
147
- this.stop();
148
- if (this.element && this.element.corruptedTextInstance === this) {
149
- delete this.element.corruptedTextInstance;
150
- }
151
- this.element = null;
152
- }
153
-
154
- /**
155
- * Main animation loop - cycles through text variants
156
- * @private
157
- */
158
- animate() {
159
- if (!this.isAnimating) return;
160
-
161
- const variant = this.availableVariants[this.currentVariantIndex];
162
- this.corruptToText(variant.text, () => {
163
- // Move to next variant in cycle
164
- this.currentVariantIndex = (this.currentVariantIndex + 1) % this.availableVariants.length;
165
-
166
- // Check if animation should stop
167
- if (!this.options.loop && this.currentVariantIndex === 0) {
168
- // One full cycle complete - settle on final text
169
- const finalText = this.options.finalText || this.variants.english;
170
- this.corruptToText(finalText, () => {
171
- this.isAnimating = false;
172
- });
173
- return;
174
- }
175
-
176
- // Continue animation to next variant
177
- this._animateTimeoutId = setTimeout(() => {
178
- if (this.isAnimating) {
179
- this.animate();
180
- }
181
- }, this.options.duration / this.availableVariants.length);
182
- });
183
- }
184
-
185
- /**
186
- * Corrupt the current text to a target text with progressive reveal
187
- *
188
- * @param {string} targetText - The text to reveal through corruption
189
- * @param {Function} callback - Called when corruption is complete
190
- * @private
191
- */
192
- corruptToText(targetText, callback) {
193
- const currentText = this.element.textContent.trim();
194
- const maxLength = Math.max(currentText.length, targetText.length);
195
- const steps = 20; // Number of corruption animation steps
196
- let step = 0;
197
-
198
- // Character sets for corruption effect (from CORRUPTED_THEME_SPEC.md)
199
- const katakana = 'アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワヲン';
200
- const hiragana = 'あいうえおかきくけこさしすせそたちつてとなにぬねのはひふへほまみむめもやゆよらりるれろわをん';
201
- const romaji = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
202
- const symbols = '0123456789!@#$%^&*()_+-=[]{}|;:,.<>?~`';
203
-
204
- // Combined corruption character set
205
- const allCorruptChars = katakana + hiragana + romaji + symbols;
206
-
207
- const corrupt = () => {
208
- if (step >= steps) {
209
- // Animation complete - set final text
210
- this.element.textContent = targetText;
211
- if (callback) callback();
212
- return;
213
- }
214
-
215
- // Generate corrupted text with progressive reveal
216
- let corrupted = '';
217
- for (let i = 0; i < maxLength; i++) {
218
- if (i < targetText.length && step > steps * 0.7) {
219
- // Last 30% of animation - start revealing target text
220
- const revealProgress = (step - steps * 0.7) / (steps * 0.3);
221
- if (Math.random() < revealProgress) {
222
- corrupted += targetText[i];
223
- } else {
224
- corrupted += allCorruptChars[Math.floor(Math.random() * allCorruptChars.length)];
225
- }
226
- } else {
227
- // First 70% - full random corruption
228
- corrupted += allCorruptChars[Math.floor(Math.random() * allCorruptChars.length)];
229
- }
230
- }
231
-
232
- this.element.textContent = corrupted;
233
- step++;
234
-
235
- // Schedule next corruption step
236
- this.animationFrame = requestAnimationFrame(() => {
237
- this._corruptTimeoutId = setTimeout(corrupt, this.options.cycleDelay);
238
- });
239
- };
240
-
241
- corrupt();
242
- }
243
-
244
- /**
245
- * Restart the animation from the beginning
246
- * @public
247
- */
248
- restart() {
249
- this.stop();
250
- this.currentVariantIndex = 0;
251
- this.start();
252
- }
253
-
254
- /**
255
- * Stop animation and settle on a specific text
256
- *
257
- * @param {string} [finalText] - Text to settle on (defaults to english variant)
258
- * @public
259
- */
260
- settle(finalText) {
261
- this.stop();
262
- this.corruptToText(finalText || this.variants.english, () => {
263
- this.isAnimating = false;
264
- });
265
- }
266
- }
267
-
268
- /**
269
- * Auto-initialize all elements with the 'corrupted-multilang' class
270
- *
271
- * This function is automatically called on DOM ready and can be called
272
- * manually to initialize dynamically added elements.
273
- *
274
- * @public
275
- */
276
- function initCorruptedText() {
277
- document.querySelectorAll('.corrupted-multilang').forEach(element => {
278
- if (!element.corruptedTextInstance) {
279
- element.corruptedTextInstance = new CorruptedText(element);
280
- }
281
- });
282
- }
283
-
284
- // Auto-initialize on DOM ready
285
- if (document.readyState === 'loading') {
286
- document.addEventListener('DOMContentLoaded', initCorruptedText);
287
- } else {
288
- initCorruptedText();
289
- }
290
-
291
- // Export for both ES6 modules and CommonJS
292
- if (typeof module !== 'undefined' && module.exports) {
293
- module.exports = { CorruptedText, initCorruptedText };
294
- }
295
-
296
- // Export for ES6 modules
297
- if (typeof exports !== 'undefined') {
298
- exports.CorruptedText = CorruptedText;
299
- exports.initCorruptedText = initCorruptedText;
300
- }