@whykusanagi/corrupted-theme 0.1.8 → 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.
- package/CHANGELOG.md +28 -0
- package/README.md +139 -3
- package/docs/COMPONENTS_REFERENCE.md +205 -2
- package/docs/governance/VERSION_MANAGEMENT.md +2 -2
- package/docs/governance/VERSION_REFERENCES.md +11 -7
- package/docs/platforms/NPM_PACKAGE.md +7 -6
- package/examples/advanced/glsl-vortex.html +0 -1
- package/examples/advanced/particles-bg.html +0 -1
- package/examples/basic/corrupted-text.html +2 -3
- package/examples/basic/typing-animation.html +136 -55
- package/examples/button.html +1 -2
- package/examples/card.html +1 -2
- package/examples/extensions-showcase.html +0 -1
- package/examples/form.html +1 -2
- package/examples/index.html +2 -3
- package/examples/layout.html +1 -2
- package/examples/nikke-team-builder.html +1 -2
- package/examples/showcase-complete.html +2 -3
- package/examples/showcase.html +1 -2
- package/package.json +1 -2
- package/src/core/typing-animation.js +274 -106
- package/src/css/theme.css +0 -15
- package/src/lib/corrupted-text.js +127 -36
- package/docs/ROADMAP.md +0 -266
- package/examples/advanced/nsfw-corruption.html +0 -348
- package/src/core/corrupted-text.js +0 -300
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Typing Animation with Buffer Corruption
|
|
3
3
|
*
|
|
4
|
-
* Simulates typing text with "buffer corruption" - phrases flickering
|
|
5
|
-
* as the neural network
|
|
6
|
-
*
|
|
4
|
+
* Simulates typing text with "buffer corruption" - phrases continuously flickering
|
|
5
|
+
* to the right of the revealed text as the neural network decodes corrupted data,
|
|
6
|
+
* before finally settling on stable readable output.
|
|
7
7
|
*
|
|
8
8
|
* Implements Pattern 2 (Phrase Flickering / Buffer Corruption) from CORRUPTED_THEME_SPEC.md
|
|
9
9
|
*
|
|
10
|
+
* Key design: char-advance and buffer-flicker run on **independent** timers so the
|
|
11
|
+
* buffer is always visible while typing is in progress, not probabilistic.
|
|
12
|
+
*
|
|
10
13
|
* @class TypingAnimation
|
|
11
|
-
* @version 1.
|
|
14
|
+
* @version 1.1.0
|
|
12
15
|
* @author whykusanagi
|
|
13
16
|
* @license MIT
|
|
14
17
|
*
|
|
@@ -16,8 +19,9 @@
|
|
|
16
19
|
* ```javascript
|
|
17
20
|
* const element = document.querySelector('.typing-text');
|
|
18
21
|
* const typing = new TypingAnimation(element, {
|
|
19
|
-
*
|
|
20
|
-
*
|
|
22
|
+
* duration: 2000, // 2s total regardless of text length
|
|
23
|
+
* loop: true,
|
|
24
|
+
* loopDelay: 1500,
|
|
21
25
|
* // nsfw: false (default - SFW phrases only)
|
|
22
26
|
* });
|
|
23
27
|
*
|
|
@@ -36,35 +40,66 @@
|
|
|
36
40
|
* @see corruption-phrases.js - Phrase library with SFW/NSFW split
|
|
37
41
|
*/
|
|
38
42
|
|
|
39
|
-
import { getRandomPhrase, getRandomPhraseByCategory } from './corruption-phrases.js';
|
|
40
|
-
|
|
41
43
|
class TypingAnimation {
|
|
44
|
+
/**
|
|
45
|
+
* Module-scope flag: fire the glitchChance deprecation warning at most once
|
|
46
|
+
* per page load, regardless of how many TypingAnimation instances are created.
|
|
47
|
+
* @type {boolean}
|
|
48
|
+
*/
|
|
49
|
+
static _warnedGlitchChance = false;
|
|
50
|
+
|
|
42
51
|
/**
|
|
43
52
|
* Creates a new TypingAnimation instance
|
|
44
53
|
*
|
|
45
54
|
* @param {HTMLElement} element - The DOM element to animate
|
|
46
55
|
* @param {Object} [options={}] - Configuration options
|
|
47
|
-
* @param {number} [options.
|
|
48
|
-
*
|
|
49
|
-
* @param {number} [options.
|
|
56
|
+
* @param {number|null} [options.duration=null] - Total ms for one typing pass.
|
|
57
|
+
* When set, char interval = max(33, duration/length). Takes priority over typingSpeed.
|
|
58
|
+
* @param {number} [options.typingSpeed=12] - Chars/sec; used only when duration is null.
|
|
59
|
+
* @param {boolean} [options.bufferEnabled=true] - Show always-on buffer corruption phrase.
|
|
60
|
+
* Set false for a clean typewriter effect with no buffer.
|
|
61
|
+
* @param {number} [options.bufferFlickerSpeed=100] - ms between buffer phrase swaps
|
|
62
|
+
* (independent of char-advance rate).
|
|
63
|
+
* @param {boolean} [options.loop=false] - Automatically restart after loopDelay ms.
|
|
64
|
+
* @param {number} [options.loopDelay=1500] - ms to hold settled text before restarting.
|
|
50
65
|
* @param {boolean} [options.nsfw=false] - Enable NSFW phrases (explicit opt-in required)
|
|
51
66
|
* @param {Function} [options.onComplete=null] - Callback when typing completes
|
|
67
|
+
* @param {number} [options.glitchChance] - DEPRECATED. Ignored; buffer is always-on.
|
|
68
|
+
* Fires a one-time console.warn per page load. Use bufferEnabled: false to disable buffer.
|
|
52
69
|
*/
|
|
53
70
|
constructor(element, options = {}) {
|
|
54
71
|
this.element = element;
|
|
72
|
+
|
|
73
|
+
// Deprecation warning for glitchChance — fires at most once per page load
|
|
74
|
+
if (options.glitchChance !== undefined && !TypingAnimation._warnedGlitchChance) {
|
|
75
|
+
console.warn(
|
|
76
|
+
"TypingAnimation: 'glitchChance' is deprecated and ignored — buffer is always-on now. " +
|
|
77
|
+
"Use bufferEnabled: false to disable."
|
|
78
|
+
);
|
|
79
|
+
TypingAnimation._warnedGlitchChance = true;
|
|
80
|
+
}
|
|
81
|
+
|
|
55
82
|
this.options = {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
83
|
+
duration: options.duration ?? null,
|
|
84
|
+
typingSpeed: options.typingSpeed ?? 12,
|
|
85
|
+
bufferEnabled: options.bufferEnabled ?? true,
|
|
86
|
+
bufferFlickerSpeed: options.bufferFlickerSpeed ?? 100,
|
|
87
|
+
loop: options.loop ?? false,
|
|
88
|
+
loopDelay: options.loopDelay ?? 1500,
|
|
89
|
+
nsfw: options.nsfw ?? false,
|
|
90
|
+
onComplete: options.onComplete ?? null,
|
|
62
91
|
};
|
|
63
92
|
|
|
64
|
-
|
|
65
|
-
this.
|
|
66
|
-
this.
|
|
67
|
-
this.
|
|
93
|
+
// Instance state
|
|
94
|
+
this.content = '';
|
|
95
|
+
this.displayedLen = 0;
|
|
96
|
+
this.done = false;
|
|
97
|
+
this.currentBufferPhrase = null;
|
|
98
|
+
|
|
99
|
+
// Three independent timer IDs (replacing the old single intervalId)
|
|
100
|
+
this.charIntervalId = null;
|
|
101
|
+
this.flickerIntervalId = null;
|
|
102
|
+
this.loopTimeoutId = null;
|
|
68
103
|
}
|
|
69
104
|
|
|
70
105
|
/**
|
|
@@ -181,60 +216,120 @@ class TypingAnimation {
|
|
|
181
216
|
'▲', '▼', '◄', '►', '◊', '○', '●', '◘'
|
|
182
217
|
];
|
|
183
218
|
|
|
219
|
+
// ---------------------------------------------------------------------------
|
|
220
|
+
// Public API
|
|
221
|
+
// ---------------------------------------------------------------------------
|
|
222
|
+
|
|
184
223
|
/**
|
|
185
224
|
* Start typing animation with buffer corruption
|
|
186
225
|
*
|
|
226
|
+
* Clears any running timers, resets state, then kicks off two independent
|
|
227
|
+
* intervals: one for char-advance, one for buffer-phrase flicker.
|
|
228
|
+
*
|
|
187
229
|
* @param {string} content - The final text to reveal
|
|
188
230
|
* @public
|
|
189
231
|
*/
|
|
190
232
|
start(content) {
|
|
191
|
-
this.
|
|
233
|
+
this._clearTimers();
|
|
234
|
+
|
|
235
|
+
this.content = content;
|
|
192
236
|
this.displayedLen = 0;
|
|
193
|
-
this.done
|
|
237
|
+
this.done = false;
|
|
194
238
|
|
|
195
|
-
|
|
196
|
-
|
|
239
|
+
// Seed initial buffer phrase so first render() is not empty
|
|
240
|
+
if (this.options.bufferEnabled) {
|
|
241
|
+
this.currentBufferPhrase = this.getRandomCorruption();
|
|
197
242
|
}
|
|
198
243
|
|
|
199
|
-
|
|
244
|
+
// Render first frame immediately (no 1-tick blank flash)
|
|
245
|
+
this.render();
|
|
246
|
+
|
|
247
|
+
// Char-advance timer
|
|
248
|
+
const charInterval = this._computeCharInterval();
|
|
249
|
+
this.charIntervalId = setInterval(() => this.tick(), charInterval);
|
|
250
|
+
|
|
251
|
+
// Buffer-flicker timer (independent of char advance)
|
|
252
|
+
if (this.options.bufferEnabled) {
|
|
253
|
+
this.flickerIntervalId = setInterval(() => {
|
|
254
|
+
this.currentBufferPhrase = this.getRandomCorruption();
|
|
255
|
+
this.render();
|
|
256
|
+
}, this.options.bufferFlickerSpeed);
|
|
257
|
+
}
|
|
200
258
|
}
|
|
201
259
|
|
|
202
260
|
/**
|
|
203
|
-
* Stop the typing animation
|
|
261
|
+
* Stop the typing animation, clearing all timers.
|
|
262
|
+
* Leaves current visual state intact.
|
|
204
263
|
* @public
|
|
205
264
|
*/
|
|
206
265
|
stop() {
|
|
207
|
-
|
|
208
|
-
clearInterval(this.intervalId);
|
|
209
|
-
this.intervalId = null;
|
|
210
|
-
}
|
|
266
|
+
this._clearTimers();
|
|
211
267
|
this.done = true;
|
|
212
268
|
}
|
|
213
269
|
|
|
214
270
|
/**
|
|
215
|
-
*
|
|
216
|
-
*
|
|
271
|
+
* Fully tear down this instance: stop all timers, clear the element,
|
|
272
|
+
* and release the DOM reference. After calling destroy(), this instance
|
|
273
|
+
* is not reusable — create a new TypingAnimation if you need one.
|
|
274
|
+
* @public
|
|
217
275
|
*/
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
276
|
+
destroy() {
|
|
277
|
+
this._clearTimers();
|
|
278
|
+
this.done = true;
|
|
279
|
+
this.currentBufferPhrase = null;
|
|
280
|
+
if (this.element) {
|
|
281
|
+
this.element.textContent = '';
|
|
282
|
+
this.element = null;
|
|
225
283
|
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Restart the animation from the beginning using the same content.
|
|
288
|
+
* @public
|
|
289
|
+
*/
|
|
290
|
+
restart() {
|
|
291
|
+
this.start(this.content);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Stop animation and immediately show final text (no animation).
|
|
296
|
+
*
|
|
297
|
+
* @param {string} [finalText] - Text to display (defaults to this.content)
|
|
298
|
+
* @public
|
|
299
|
+
*/
|
|
300
|
+
settle(finalText) {
|
|
301
|
+
if (!this.element) return;
|
|
302
|
+
this.stop();
|
|
303
|
+
this.currentBufferPhrase = null;
|
|
304
|
+
this.element.textContent = '';
|
|
305
|
+
const span = document.createElement('span');
|
|
306
|
+
span.style.color = '#ffffff';
|
|
307
|
+
span.textContent = finalText ?? this.content;
|
|
308
|
+
this.element.appendChild(span);
|
|
309
|
+
}
|
|
226
310
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
this.displayedLen = Math.min(this.displayedLen + charsPerTick, this.content.length);
|
|
311
|
+
// ---------------------------------------------------------------------------
|
|
312
|
+
// Internal helpers
|
|
313
|
+
// ---------------------------------------------------------------------------
|
|
231
314
|
|
|
315
|
+
/**
|
|
316
|
+
* Advance revealed text by one character and call render().
|
|
317
|
+
* Delegates to _onComplete() when the full text is revealed.
|
|
318
|
+
* @private
|
|
319
|
+
*/
|
|
320
|
+
tick() {
|
|
321
|
+
// Advance exactly one character per tick
|
|
322
|
+
this.displayedLen = Math.min(this.displayedLen + 1, this.content.length);
|
|
232
323
|
this.render();
|
|
324
|
+
|
|
325
|
+
if (this.displayedLen >= this.content.length) {
|
|
326
|
+
this._onComplete();
|
|
327
|
+
}
|
|
233
328
|
}
|
|
234
329
|
|
|
235
330
|
/**
|
|
236
|
-
* Check if typing is complete
|
|
237
|
-
* @returns {boolean}
|
|
331
|
+
* Check if typing is complete.
|
|
332
|
+
* @returns {boolean}
|
|
238
333
|
* @private
|
|
239
334
|
*/
|
|
240
335
|
isDone() {
|
|
@@ -242,8 +337,8 @@ class TypingAnimation {
|
|
|
242
337
|
}
|
|
243
338
|
|
|
244
339
|
/**
|
|
245
|
-
* Get the currently revealed portion of text
|
|
246
|
-
* @returns {string}
|
|
340
|
+
* Get the currently revealed portion of text.
|
|
341
|
+
* @returns {string}
|
|
247
342
|
* @private
|
|
248
343
|
*/
|
|
249
344
|
getDisplayed() {
|
|
@@ -251,18 +346,140 @@ class TypingAnimation {
|
|
|
251
346
|
}
|
|
252
347
|
|
|
253
348
|
/**
|
|
254
|
-
*
|
|
349
|
+
* Compute char-advance interval in ms.
|
|
350
|
+
*
|
|
351
|
+
* Priority: duration (fixed total time) → typingSpeed (chars/sec).
|
|
352
|
+
* Floored at 33ms (~30fps) to avoid burning unnecessary frames.
|
|
353
|
+
*
|
|
354
|
+
* @returns {number} Interval in ms
|
|
355
|
+
* @private
|
|
356
|
+
*/
|
|
357
|
+
_computeCharInterval() {
|
|
358
|
+
const { duration, typingSpeed } = this.options;
|
|
359
|
+
if (duration !== null && this.content.length > 0) {
|
|
360
|
+
return Math.max(33, duration / this.content.length);
|
|
361
|
+
}
|
|
362
|
+
return Math.max(33, 1000 / typingSpeed);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Clear all active timers. Safe to call repeatedly.
|
|
367
|
+
*
|
|
368
|
+
* NOTE: _onComplete() intentionally does NOT call this method — it needs
|
|
369
|
+
* to clear char+flicker intervals while preserving loopTimeoutId so the
|
|
370
|
+
* loop restart can be scheduled. If you add a fourth timer here, update
|
|
371
|
+
* _onComplete() to clear or preserve it as appropriate.
|
|
372
|
+
*
|
|
373
|
+
* @private
|
|
374
|
+
*/
|
|
375
|
+
_clearTimers() {
|
|
376
|
+
if (this.charIntervalId !== null) {
|
|
377
|
+
clearInterval(this.charIntervalId);
|
|
378
|
+
this.charIntervalId = null;
|
|
379
|
+
}
|
|
380
|
+
if (this.flickerIntervalId !== null) {
|
|
381
|
+
clearInterval(this.flickerIntervalId);
|
|
382
|
+
this.flickerIntervalId = null;
|
|
383
|
+
}
|
|
384
|
+
if (this.loopTimeoutId !== null) {
|
|
385
|
+
clearTimeout(this.loopTimeoutId);
|
|
386
|
+
this.loopTimeoutId = null;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Called when the full text has been revealed.
|
|
392
|
+
*
|
|
393
|
+
* Stops char and flicker intervals, hides buffer, fires onComplete callback,
|
|
394
|
+
* then either schedules a loop restart or marks animation as done.
|
|
395
|
+
* @private
|
|
396
|
+
*/
|
|
397
|
+
_onComplete() {
|
|
398
|
+
// Stop char and flicker intervals (NOT loopTimeoutId — set below)
|
|
399
|
+
if (this.charIntervalId !== null) {
|
|
400
|
+
clearInterval(this.charIntervalId);
|
|
401
|
+
this.charIntervalId = null;
|
|
402
|
+
}
|
|
403
|
+
if (this.flickerIntervalId !== null) {
|
|
404
|
+
clearInterval(this.flickerIntervalId);
|
|
405
|
+
this.flickerIntervalId = null;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Hide buffer, render settled state
|
|
409
|
+
this.currentBufferPhrase = null;
|
|
410
|
+
this.render();
|
|
411
|
+
|
|
412
|
+
// Fire user callback
|
|
413
|
+
if (this.options.onComplete) {
|
|
414
|
+
this.options.onComplete();
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
if (this.options.loop) {
|
|
418
|
+
// Schedule restart; store ID so stop()/destroy() can cancel it
|
|
419
|
+
this.loopTimeoutId = setTimeout(() => {
|
|
420
|
+
this.loopTimeoutId = null;
|
|
421
|
+
this.start(this.content);
|
|
422
|
+
}, this.options.loopDelay);
|
|
423
|
+
} else {
|
|
424
|
+
this.done = true;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Render the current state of the typing animation.
|
|
430
|
+
*
|
|
431
|
+
* DOM layout while typing:
|
|
432
|
+
* [revealed-text (white)] [space] [bufferPhrase (magenta/purple)]
|
|
433
|
+
*
|
|
434
|
+
* DOM layout when done:
|
|
435
|
+
* [revealed-text (white)]
|
|
436
|
+
*
|
|
437
|
+
* All DOM construction uses createElement/textContent/appendChild —
|
|
438
|
+
* no innerHTML, preserving XSS hardening from v0.1.7.
|
|
439
|
+
*
|
|
440
|
+
* @private
|
|
441
|
+
*/
|
|
442
|
+
render() {
|
|
443
|
+
if (!this.element) return;
|
|
444
|
+
const isDone = this.isDone();
|
|
445
|
+
const displayed = this.getDisplayed();
|
|
446
|
+
|
|
447
|
+
// Clear and rebuild safely (no innerHTML)
|
|
448
|
+
this.element.textContent = '';
|
|
449
|
+
|
|
450
|
+
// Revealed text span (white)
|
|
451
|
+
const textSpan = document.createElement('span');
|
|
452
|
+
textSpan.style.color = '#ffffff';
|
|
453
|
+
textSpan.textContent = displayed;
|
|
454
|
+
this.element.appendChild(textSpan);
|
|
455
|
+
|
|
456
|
+
// Buffer corruption phrase — always present while typing, if enabled and available
|
|
457
|
+
if (!isDone && this.options.bufferEnabled && this.currentBufferPhrase) {
|
|
458
|
+
this.element.appendChild(document.createTextNode(' '));
|
|
459
|
+
this.element.appendChild(this.currentBufferPhrase.cloneNode(true));
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Get random buffer corruption phrase as a colored <span> element.
|
|
255
465
|
*
|
|
256
466
|
* Samples from phrase buffer based on SFW/NSFW mode and renders
|
|
257
467
|
* with color-coded corruption aesthetic.
|
|
258
468
|
*
|
|
469
|
+
* Category distribution:
|
|
470
|
+
* 0–29% Japanese phrase (SFW or NSFW per options.nsfw)
|
|
471
|
+
* 30–49% English phrase
|
|
472
|
+
* 50–64% Romaji phrase
|
|
473
|
+
* 65–79% Symbol (always SFW)
|
|
474
|
+
* 80–99% Block character (always SFW)
|
|
475
|
+
*
|
|
259
476
|
* Color scheme:
|
|
260
|
-
*
|
|
261
|
-
*
|
|
262
|
-
*
|
|
263
|
-
*
|
|
477
|
+
* SFW phrases: Magenta (#d94f90) — playful corruption
|
|
478
|
+
* NSFW phrases: Purple (#8b5cf6) — deep/intimate corruption
|
|
479
|
+
* Symbols: Magenta (#d94f90)
|
|
480
|
+
* Blocks: Red (#ff0000) — terminal/critical state
|
|
264
481
|
*
|
|
265
|
-
* @returns {
|
|
482
|
+
* @returns {HTMLSpanElement} Colored span with textContent set
|
|
266
483
|
* @private
|
|
267
484
|
*/
|
|
268
485
|
getRandomCorruption() {
|
|
@@ -300,74 +517,25 @@ class TypingAnimation {
|
|
|
300
517
|
text = romajiSet[Math.floor(Math.random() * romajiSet.length)];
|
|
301
518
|
color = phraseColor;
|
|
302
519
|
} else if (r < 0.80) {
|
|
303
|
-
// Symbols
|
|
520
|
+
// Symbols — decorative corruption (always SFW)
|
|
304
521
|
text = TypingAnimation.SYMBOLS[
|
|
305
522
|
Math.floor(Math.random() * TypingAnimation.SYMBOLS.length)
|
|
306
523
|
];
|
|
307
524
|
color = '#d94f90';
|
|
308
525
|
} else {
|
|
309
|
-
// Block chars
|
|
526
|
+
// Block chars — terminal/critical state (always SFW)
|
|
310
527
|
text = TypingAnimation.BLOCKS[
|
|
311
528
|
Math.floor(Math.random() * TypingAnimation.BLOCKS.length)
|
|
312
529
|
];
|
|
313
530
|
color = '#ff0000';
|
|
314
531
|
}
|
|
315
532
|
|
|
316
|
-
//
|
|
533
|
+
// Build DOM element (XSS-safe — no innerHTML)
|
|
317
534
|
const span = document.createElement('span');
|
|
318
535
|
span.style.color = color;
|
|
319
536
|
span.textContent = text;
|
|
320
537
|
return span;
|
|
321
538
|
}
|
|
322
|
-
|
|
323
|
-
/**
|
|
324
|
-
* Render the current state of the typing animation
|
|
325
|
-
*
|
|
326
|
-
* Displays revealed text (white) and occasional buffer corruption
|
|
327
|
-
* phrases (magenta or purple) simulating neural decoding process.
|
|
328
|
-
*
|
|
329
|
-
* @private
|
|
330
|
-
*/
|
|
331
|
-
render() {
|
|
332
|
-
const displayed = this.getDisplayed();
|
|
333
|
-
|
|
334
|
-
// Clear and rebuild using safe DOM methods (no innerHTML)
|
|
335
|
-
this.element.textContent = '';
|
|
336
|
-
|
|
337
|
-
// Stable revealed text (white)
|
|
338
|
-
const textSpan = document.createElement('span');
|
|
339
|
-
textSpan.style.color = '#ffffff';
|
|
340
|
-
textSpan.textContent = displayed;
|
|
341
|
-
this.element.appendChild(textSpan);
|
|
342
|
-
|
|
343
|
-
// Add buffer corruption element at the "cursor" position
|
|
344
|
-
if (!this.isDone() && Math.random() < this.options.glitchChance) {
|
|
345
|
-
this.element.appendChild(this.getRandomCorruption());
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
/**
|
|
350
|
-
* Stop animation and immediately show final text
|
|
351
|
-
*
|
|
352
|
-
* @param {string} [finalText] - Text to display (defaults to content)
|
|
353
|
-
* @public
|
|
354
|
-
*/
|
|
355
|
-
settle(finalText) {
|
|
356
|
-
this.stop();
|
|
357
|
-
const span = document.createElement('span');
|
|
358
|
-
span.style.color = '#ffffff';
|
|
359
|
-
span.textContent = finalText || this.content;
|
|
360
|
-
this.element.textContent = '';
|
|
361
|
-
this.element.appendChild(span);
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
/**
|
|
365
|
-
* Restart the animation from the beginning
|
|
366
|
-
* @public
|
|
367
|
-
*/
|
|
368
|
-
restart() {
|
|
369
|
-
this.start(this.content);
|
|
370
|
-
}
|
|
371
539
|
}
|
|
372
540
|
|
|
373
541
|
/**
|
package/src/css/theme.css
CHANGED
|
@@ -28,21 +28,6 @@
|
|
|
28
28
|
/* ========== EXTENSION COMPONENTS ========== */
|
|
29
29
|
@import './extensions.css';
|
|
30
30
|
|
|
31
|
-
/* ========== RESPONSIVE GRID SYSTEM ========== */
|
|
32
|
-
|
|
33
|
-
.container {
|
|
34
|
-
position: relative;
|
|
35
|
-
z-index: 1;
|
|
36
|
-
display: grid;
|
|
37
|
-
grid-template-columns: 1fr 1fr;
|
|
38
|
-
gap: 4rem;
|
|
39
|
-
align-items: center;
|
|
40
|
-
min-height: 100vh;
|
|
41
|
-
padding: 3rem;
|
|
42
|
-
max-width: 1400px;
|
|
43
|
-
margin: 0 auto;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
31
|
.content {
|
|
47
32
|
padding: 2.5rem;
|
|
48
33
|
background: var(--glass);
|