@whykusanagi/corrupted-theme 0.1.6 → 0.1.7

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.
@@ -0,0 +1,223 @@
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>Corrupted Theme - Interactive Components Demo</title>
7
+ <link rel="stylesheet" href="../src/css/theme.css">
8
+ <style>
9
+ body {
10
+ background: var(--bg);
11
+ color: var(--text);
12
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
13
+ line-height: 1.6;
14
+ padding: 0;
15
+ margin: 0;
16
+ }
17
+
18
+ .demo {
19
+ max-width: 900px;
20
+ margin: 0 auto;
21
+ padding: var(--spacing-xl);
22
+ }
23
+
24
+ .demo h1 {
25
+ color: var(--accent);
26
+ margin-bottom: var(--spacing-sm);
27
+ }
28
+
29
+ .demo-section {
30
+ margin-bottom: var(--spacing-2xl);
31
+ }
32
+
33
+ .demo-section h2 {
34
+ color: var(--accent-light);
35
+ border-bottom: 1px solid var(--border);
36
+ padding-bottom: var(--spacing-sm);
37
+ margin-bottom: var(--spacing-lg);
38
+ }
39
+
40
+ .demo-row {
41
+ display: flex;
42
+ gap: var(--spacing-md);
43
+ flex-wrap: wrap;
44
+ margin-bottom: var(--spacing-md);
45
+ }
46
+
47
+ /* Carousel demo images (placeholder) */
48
+ .carousel-slide {
49
+ background: var(--glass);
50
+ display: flex;
51
+ align-items: center;
52
+ justify-content: center;
53
+ min-height: 250px;
54
+ font-size: 1.5rem;
55
+ color: var(--text-secondary);
56
+ }
57
+ </style>
58
+ </head>
59
+ <body>
60
+
61
+ <div class="demo">
62
+ <h1>Interactive Components</h1>
63
+ <p style="color: var(--text-secondary); margin-bottom: var(--spacing-xl);">
64
+ All components use <code>data-ct-*</code> attributes for zero-JS-config initialization.
65
+ </p>
66
+
67
+ <!-- ==================== MODAL ==================== -->
68
+ <div class="demo-section">
69
+ <h2>Modal</h2>
70
+ <div class="demo-row">
71
+ <button class="btn" data-ct-toggle="modal" data-ct-target="#demo-modal">Open Modal</button>
72
+ </div>
73
+
74
+ <div class="modal-overlay" id="demo-modal">
75
+ <div class="modal">
76
+ <div class="modal-header">
77
+ <h3 class="modal-title">Corruption Protocol</h3>
78
+ <button class="modal-close">&times;</button>
79
+ </div>
80
+ <div class="modal-body">
81
+ <p>This modal was opened via <code>data-ct-toggle="modal"</code>.</p>
82
+ <p>Close it by clicking the X, clicking outside, or pressing Escape.</p>
83
+ </div>
84
+ <div class="modal-footer">
85
+ <button class="btn secondary" data-ct-toggle="modal" data-ct-target="#demo-modal">Cancel</button>
86
+ <button class="btn" data-ct-toggle="modal" data-ct-target="#demo-modal">Confirm</button>
87
+ </div>
88
+ </div>
89
+ </div>
90
+ </div>
91
+
92
+ <!-- ==================== DROPDOWN ==================== -->
93
+ <div class="demo-section">
94
+ <h2>Dropdown</h2>
95
+ <div class="demo-row">
96
+ <div class="dropdown">
97
+ <button class="dropdown-toggle" data-ct-toggle="dropdown">Actions</button>
98
+ <div class="dropdown-menu">
99
+ <a href="#" class="dropdown-item">Edit</a>
100
+ <a href="#" class="dropdown-item">Duplicate</a>
101
+ <div class="dropdown-divider"></div>
102
+ <a href="#" class="dropdown-item">Delete</a>
103
+ </div>
104
+ </div>
105
+
106
+ <div class="dropdown">
107
+ <button class="dropdown-toggle" data-ct-toggle="dropdown">Settings</button>
108
+ <div class="dropdown-menu">
109
+ <a href="#" class="dropdown-item">Profile</a>
110
+ <a href="#" class="dropdown-item">Preferences</a>
111
+ <a href="#" class="dropdown-item">Logout</a>
112
+ </div>
113
+ </div>
114
+ </div>
115
+ <p style="color: var(--text-secondary); font-size: 0.9rem;">Click outside to close. Multiple dropdowns don't conflict.</p>
116
+ </div>
117
+
118
+ <!-- ==================== TABS ==================== -->
119
+ <div class="demo-section">
120
+ <h2>Tabs</h2>
121
+ <div class="tabs">
122
+ <button class="tab active" data-ct-target="#tab-overview">Overview</button>
123
+ <button class="tab" data-ct-target="#tab-code">Code</button>
124
+ <button class="tab" data-ct-target="#tab-api">API</button>
125
+ </div>
126
+
127
+ <div class="tab-content active" id="tab-overview">
128
+ <div class="glass-card" style="padding: 1.5rem; margin-top: var(--spacing-md);">
129
+ <h3>Overview</h3>
130
+ <p>Tabs switch between panels using <code>data-ct-target</code> pointing to panel IDs.</p>
131
+ </div>
132
+ </div>
133
+ <div class="tab-content" id="tab-code">
134
+ <div class="glass-card" style="padding: 1.5rem; margin-top: var(--spacing-md);">
135
+ <h3>Code Example</h3>
136
+ <pre><code>&lt;div class="tabs"&gt;
137
+ &lt;button class="tab active" data-ct-target="#panel-1"&gt;Tab 1&lt;/button&gt;
138
+ &lt;button class="tab" data-ct-target="#panel-2"&gt;Tab 2&lt;/button&gt;
139
+ &lt;/div&gt;</code></pre>
140
+ </div>
141
+ </div>
142
+ <div class="tab-content" id="tab-api">
143
+ <div class="glass-card" style="padding: 1.5rem; margin-top: var(--spacing-md);">
144
+ <h3>API Reference</h3>
145
+ <p>Import <code>destroyComponents()</code> to tear down all managers.</p>
146
+ </div>
147
+ </div>
148
+ </div>
149
+
150
+ <!-- ==================== COLLAPSE ==================== -->
151
+ <div class="demo-section">
152
+ <h2>Collapse</h2>
153
+ <button class="btn secondary" data-ct-toggle="collapse" data-ct-target="#collapse-details">Toggle Details</button>
154
+ <div class="collapse" id="collapse-details" style="margin-top: var(--spacing-md);">
155
+ <div class="glass-card" style="padding: 1.5rem;">
156
+ <p>This section is toggled via <code>data-ct-toggle="collapse"</code>.</p>
157
+ <p>The existing <code>.collapse.show</code> CSS class controls visibility.</p>
158
+ </div>
159
+ </div>
160
+ </div>
161
+
162
+ <!-- ==================== ACCORDION ==================== -->
163
+ <div class="demo-section">
164
+ <h2>Accordion</h2>
165
+ <div class="accordion">
166
+ <div class="accordion-item active">
167
+ <div class="accordion-header">What is Corrupted Theme?</div>
168
+ <div class="accordion-body">
169
+ <p>A visual aesthetic NPM package simulating neural corruption, data degradation, and system instability.</p>
170
+ </div>
171
+ </div>
172
+ <div class="accordion-item">
173
+ <div class="accordion-header">How do I install it?</div>
174
+ <div class="accordion-body">
175
+ <p><code>npm install @whykusanagi/corrupted-theme</code></p>
176
+ </div>
177
+ </div>
178
+ <div class="accordion-item">
179
+ <div class="accordion-header">Does it require JavaScript?</div>
180
+ <div class="accordion-body">
181
+ <p>CSS works standalone. JavaScript adds interactivity (accordion toggle, modal, dropdown, etc.).</p>
182
+ </div>
183
+ </div>
184
+ </div>
185
+ </div>
186
+
187
+ <!-- ==================== CAROUSEL ==================== -->
188
+ <div class="demo-section">
189
+ <h2>Carousel</h2>
190
+ <div class="carousel" data-ct-autoplay data-ct-interval="4000">
191
+ <div class="carousel-inner">
192
+ <div class="carousel-slide active">Slide 1 — Corruption Injection</div>
193
+ <div class="carousel-slide">Slide 2 — Neural Degradation</div>
194
+ <div class="carousel-slide">Slide 3 — System Instability</div>
195
+ </div>
196
+ </div>
197
+ </div>
198
+
199
+ <!-- ==================== TOAST ==================== -->
200
+ <div class="demo-section">
201
+ <h2>Toast Notifications</h2>
202
+ <div class="demo-row">
203
+ <button class="btn" onclick="window._showToast('success')">Success</button>
204
+ <button class="btn secondary" onclick="window._showToast('warning')">Warning</button>
205
+ <button class="btn" style="background: var(--accent-secondary);" onclick="window._showToast('error')">Error</button>
206
+ <button class="btn ghost" onclick="window._showToast('info')">Info</button>
207
+ </div>
208
+ </div>
209
+ </div>
210
+
211
+ <!-- Scripts -->
212
+ <script type="module">
213
+ import { showToast, toast } from '../src/lib/components.js';
214
+ import '../src/lib/carousel.js'; // Auto-initializes carousels with data-ct-autoplay
215
+
216
+ // Toast demo helper
217
+ window._showToast = (type) => {
218
+ toast[type](`This is a ${type} notification.`);
219
+ };
220
+ </script>
221
+
222
+ </body>
223
+ </html>
@@ -187,9 +187,12 @@
187
187
  <a href="card.html"><i class="fas fa-square"></i> Cards</a>
188
188
  <a href="form.html"><i class="fas fa-edit"></i> Forms</a>
189
189
  <a href="layout.html" class="active"><i class="fas fa-columns"></i> Layouts</a>
190
+ <a href="basic/corrupted-text.html"><i class="fas fa-terminal"></i> Character Corruption</a>
191
+ <a href="basic/typing-animation.html"><i class="fas fa-keyboard"></i> Buffer Corruption</a>
192
+ <a href="advanced/nsfw-corruption.html"><i class="fas fa-exclamation-triangle"></i> NSFW (18+)</a>
190
193
  </div>
191
194
  </li>
192
- <li><a href="../showcase-complete.html"><i class="fas fa-book"></i> Docs</a></li>
195
+ <li><a href="showcase-complete.html"><i class="fas fa-book"></i> Docs</a></li>
193
196
  </ul>
194
197
  </div>
195
198
  </nav>
@@ -511,7 +514,7 @@
511
514
  </section>
512
515
 
513
516
  <footer style="text-align: center; padding: var(--spacing-2xl) var(--spacing-lg); margin-top: var(--spacing-2xl); border-top: 1px solid var(--border); color: var(--text-secondary);">
514
- <p>Layout Patterns Documentation • Corrupted Theme v0.1.4</p>
517
+ <p>Layout Patterns Documentation • Corrupted Theme v0.1.7</p>
515
518
  <p style="font-size: 0.875rem; margin-top: var(--spacing-md);">
516
519
  <a href="index.html" style="color: var(--accent); text-decoration: none;">← Back to Showcase</a>
517
520
  </p>
@@ -194,9 +194,12 @@
194
194
  <a href="card.html"><i class="fas fa-square"></i> Cards</a>
195
195
  <a href="form.html"><i class="fas fa-edit"></i> Forms</a>
196
196
  <a href="layout.html"><i class="fas fa-columns"></i> Layouts</a>
197
+ <a href="basic/corrupted-text.html"><i class="fas fa-terminal"></i> Character Corruption</a>
198
+ <a href="basic/typing-animation.html"><i class="fas fa-keyboard"></i> Buffer Corruption</a>
199
+ <a href="advanced/nsfw-corruption.html"><i class="fas fa-exclamation-triangle"></i> NSFW (18+)</a>
197
200
  </div>
198
201
  </li>
199
- <li><a href="../showcase-complete.html"><i class="fas fa-book"></i> Docs</a></li>
202
+ <li><a href="showcase-complete.html"><i class="fas fa-book"></i> Docs</a></li>
200
203
  </ul>
201
204
  </div>
202
205
  </nav>
@@ -563,10 +566,10 @@
563
566
  </section>
564
567
 
565
568
  <footer style="text-align: center; padding: var(--spacing-2xl) var(--spacing-lg); margin-top: var(--spacing-2xl); border-top: 1px solid var(--border); color: var(--text-secondary);">
566
- <p>Nikke Team Builder Example • Corrupted Theme v0.1.4</p>
569
+ <p>Nikke Team Builder Example • Corrupted Theme v0.1.7</p>
567
570
  <p style="font-size: 0.875rem; margin-top: var(--spacing-md);">
568
571
  <a href="index.html" style="color: var(--accent); text-decoration: none;">← Back to Showcase</a> •
569
- <a href="../showcase-complete.html#nikke" style="color: var(--accent); text-decoration: none;">Documentation</a>
572
+ <a href="showcase-complete.html#nikke" style="color: var(--accent); text-decoration: none;">Documentation</a>
570
573
  </p>
571
574
  </footer>
572
575
  </div>
@@ -147,9 +147,12 @@
147
147
  <a href="card.html"><i class="fas fa-square"></i> Cards</a>
148
148
  <a href="form.html"><i class="fas fa-edit"></i> Forms</a>
149
149
  <a href="layout.html"><i class="fas fa-columns"></i> Layouts</a>
150
+ <a href="basic/corrupted-text.html"><i class="fas fa-terminal"></i> Character Corruption</a>
151
+ <a href="basic/typing-animation.html"><i class="fas fa-keyboard"></i> Buffer Corruption</a>
152
+ <a href="advanced/nsfw-corruption.html"><i class="fas fa-exclamation-triangle"></i> NSFW (18+)</a>
150
153
  </div>
151
154
  </li>
152
- <li><a href="../showcase-complete.html"><i class="fas fa-book"></i> Docs</a></li>
155
+ <li><a href="showcase-complete.html"><i class="fas fa-book"></i> Docs</a></li>
153
156
  </ul>
154
157
  </div>
155
158
  </nav>
@@ -1132,7 +1135,7 @@ const theme = {
1132
1135
 
1133
1136
  <!-- Character-Level Corruption (NEW in v0.1.4) -->
1134
1137
  <section class="showcase-section" id="character-corruption">
1135
- <h2><i class="fas fa-language"></i> Character-Level Corruption <span class="badge success" style="margin-left: var(--spacing-sm);">NEW v0.1.4</span></h2>
1138
+ <h2><i class="fas fa-language"></i> Character-Level Corruption</h2>
1136
1139
  <p style="color: var(--text-secondary); margin-bottom: var(--spacing-lg);">
1137
1140
  <strong>CLI Brand Parity:</strong> Matches Celeste CLI's translation-failure aesthetic. Japanese characters are mixed <strong>INTO</strong> English words at the character level (not word replacement).
1138
1141
  </p>
@@ -1296,13 +1299,13 @@ const theme = {
1296
1299
  &lt;/h1&gt;
1297
1300
 
1298
1301
  &lt;script type="module"&gt;
1299
- import { initAutoCorruption } from './character-corruption.js';
1302
+ import { initAutoCorruption } from '@whykusanagi/corrupted-theme/character-corruption';
1300
1303
  initAutoCorruption();
1301
1304
  &lt;/script&gt;
1302
1305
 
1303
1306
  &lt;!-- Method 2: Manual JavaScript --&gt;
1304
1307
  &lt;script type="module"&gt;
1305
- import { corruptTextJapanese, INTENSITY } from './character-corruption.js';
1308
+ import { corruptTextJapanese, INTENSITY } from '@whykusanagi/corrupted-theme/character-corruption';
1306
1309
 
1307
1310
  const title = document.getElementById('title');
1308
1311
  setInterval(() => {
@@ -1314,7 +1317,7 @@ const theme = {
1314
1317
 
1315
1318
  <!-- New Bootstrap Components (v0.1.4) -->
1316
1319
  <section class="showcase-section" id="new-components">
1317
- <h2><i class="fas fa-plus-circle"></i> New Bootstrap Components <span class="badge success" style="margin-left: var(--spacing-sm);">NEW v0.1.4</span></h2>
1320
+ <h2><i class="fas fa-plus-circle"></i> Bootstrap Components</h2>
1318
1321
  <p style="color: var(--text-secondary); margin-bottom: var(--spacing-lg);">
1319
1322
  <strong>1:1 Bootstrap Parity:</strong> 25+ new components added for complete coverage. Build sites without writing custom CSS!
1320
1323
  </p>
@@ -1355,7 +1358,7 @@ const theme = {
1355
1358
  &lt;/div&gt;
1356
1359
 
1357
1360
  &lt;script type="module"&gt;
1358
- import { initAccordions } from './components.js';
1361
+ import { initAccordions } from '@whykusanagi/corrupted-theme/components-js';
1359
1362
  initAccordions(); // Auto-initializes on load
1360
1363
  &lt;/script&gt;
1361
1364
  </div>
@@ -1381,7 +1384,7 @@ const theme = {
1381
1384
 
1382
1385
  <div class="code-example" style="margin-top: var(--spacing-md);">
1383
1386
  &lt;script type="module"&gt;
1384
- import { toast } from './components.js';
1387
+ import { toast } from '@whykusanagi/corrupted-theme/components-js';
1385
1388
 
1386
1389
  // Success notification
1387
1390
  toast.success('Data saved successfully!', 'Success', 5000);
@@ -1694,9 +1697,9 @@ const theme = {
1694
1697
 
1695
1698
  <!-- Footer -->
1696
1699
  <footer style="text-align: center; padding: var(--spacing-2xl) var(--spacing-lg); margin-top: var(--spacing-2xl); border-top: 1px solid var(--border); color: var(--text-secondary);">
1697
- <p><strong>Corrupted Theme</strong> v0.1.4 • Built with <i class="fas fa-heart" style="color: var(--accent);"></i> by whykusanagi</p>
1700
+ <p><strong>Corrupted Theme</strong> v0.1.7 • Built with <i class="fas fa-heart" style="color: var(--accent);"></i> by whykusanagi</p>
1698
1701
  <p style="font-size: 0.875rem; margin-top: var(--spacing-md);">
1699
- <a href="../README.md" class="link">Documentation</a> •
1702
+ <a href="https://github.com/whykusanagi/corrupted-theme#readme" class="link">Documentation</a> •
1700
1703
  <a href="#customization" class="link">Customization</a> •
1701
1704
  <a href="https://github.com/whykusanagi/corrupted-theme" class="link">GitHub</a>
1702
1705
  </p>
@@ -1828,11 +1831,9 @@ const theme = {
1828
1831
  }
1829
1832
  });
1830
1833
  });
1831
-
1832
- document.addEventListener('DOMContentLoaded', initGlitchKanji);
1833
1834
  </script>
1834
1835
 
1835
- <!-- NEW v0.1.4: Character Corruption & Components -->
1836
+ <!-- Character Corruption & Components -->
1836
1837
  <script type="module">
1837
1838
  // Import character corruption module
1838
1839
  import { corruptTextJapanese, INTENSITY, initAutoCorruption } from '../src/lib/character-corruption.js';
@@ -1919,7 +1920,7 @@ const theme = {
1919
1920
 
1920
1921
  // Auto-show welcome message on page load
1921
1922
  setTimeout(() => {
1922
- toast.success('Welcome to Corrupted Theme v0.1.4!', 'Welcome', 5000);
1923
+ toast.success('Welcome to Corrupted Theme v0.1.7!', 'Welcome', 5000);
1923
1924
  }, 1000);
1924
1925
  </script>
1925
1926
  </body>
@@ -149,9 +149,12 @@
149
149
  <a href="card.html"><i class="fas fa-square"></i> Cards</a>
150
150
  <a href="form.html"><i class="fas fa-edit"></i> Forms</a>
151
151
  <a href="layout.html"><i class="fas fa-columns"></i> Layouts</a>
152
+ <a href="basic/corrupted-text.html"><i class="fas fa-terminal"></i> Character Corruption</a>
153
+ <a href="basic/typing-animation.html"><i class="fas fa-keyboard"></i> Buffer Corruption</a>
154
+ <a href="advanced/nsfw-corruption.html"><i class="fas fa-exclamation-triangle"></i> NSFW (18+)</a>
152
155
  </div>
153
156
  </li>
154
- <li><a href="../showcase-complete.html"><i class="fas fa-book"></i> Docs</a></li>
157
+ <li><a href="showcase-complete.html"><i class="fas fa-book"></i> Docs</a></li>
155
158
  </ul>
156
159
  </div>
157
160
  </nav>
@@ -462,9 +465,9 @@ const theme = {
462
465
 
463
466
  <!-- Footer -->
464
467
  <footer style="text-align: center; padding: var(--spacing-2xl) var(--spacing-lg); margin-top: var(--spacing-2xl); border-top: 1px solid var(--border); color: var(--text-secondary);">
465
- <p><strong>Corrupted Theme</strong> v0.1.4 • Built with <i class="fas fa-heart" style="color: var(--accent);"></i> by whykusanagi</p>
468
+ <p><strong>Corrupted Theme</strong> v0.1.7 • Built with <i class="fas fa-heart" style="color: var(--accent);"></i> by whykusanagi</p>
466
469
  <p style="font-size: 0.875rem; margin-top: var(--spacing-md);">
467
- <a href="../README.md" class="link">Documentation</a> •
470
+ <a href="https://github.com/whykusanagi/corrupted-theme#readme" class="link">Documentation</a> •
468
471
  <a href="#customization" class="link">Customization</a> •
469
472
  <a href="https://github.com/whykusanagi/corrupted-theme" class="link">GitHub</a>
470
473
  </p>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@whykusanagi/corrupted-theme",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "type": "module",
5
5
  "description": "A dark, glassmorphic design system with pink/purple accents. Perfect for creating modern, visually striking web applications.",
6
6
  "main": "src/css/theme.css",
@@ -23,7 +23,8 @@
23
23
  "./corrupted-text": "./src/lib/corrupted-text.js",
24
24
  "./corruption-loading": "./src/lib/corruption-loading.js",
25
25
  "./character-corruption": "./src/lib/character-corruption.js",
26
- "./components-js": "./src/lib/components.js"
26
+ "./components-js": "./src/lib/components.js",
27
+ "./carousel": "./src/lib/carousel.js"
27
28
  },
28
29
  "files": [
29
30
  "src",
@@ -69,12 +70,12 @@
69
70
  "author": "whykusanagi <contact@whykusanagi.xyz>",
70
71
  "license": "MIT",
71
72
  "engines": {
72
- "node": ">=14.0.0"
73
+ "node": ">=18.0.0"
73
74
  },
74
75
  "devDependencies": {
75
- "cssnano": "^6.0.0",
76
+ "cssnano": "^7.1.2",
76
77
  "postcss": "^8.4.0",
77
- "postcss-cli": "^10.0.0"
78
+ "postcss-cli": "^11.0.1"
78
79
  },
79
80
  "scripts": {
80
81
  "build": "postcss src/css/theme.css -o dist/theme.min.css",
@@ -85,6 +85,9 @@ class CorruptedText {
85
85
  this.currentVariantIndex = 0;
86
86
  this.isAnimating = false;
87
87
  this.animationFrame = null;
88
+ this._startDelayId = null;
89
+ this._animateTimeoutId = null;
90
+ this._corruptTimeoutId = null;
88
91
 
89
92
  this.init();
90
93
  }
@@ -104,7 +107,7 @@ class CorruptedText {
104
107
 
105
108
  // Start animation after configured delay
106
109
  if (this.options.startDelay > 0) {
107
- setTimeout(() => this.start(), this.options.startDelay);
110
+ this._startDelayId = setTimeout(() => this.start(), this.options.startDelay);
108
111
  } else {
109
112
  this.start();
110
113
  }
@@ -126,9 +129,26 @@ class CorruptedText {
126
129
  */
127
130
  stop() {
128
131
  this.isAnimating = false;
129
- if (this.animationFrame) {
130
- cancelAnimationFrame(this.animationFrame);
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;
131
150
  }
151
+ this.element = null;
132
152
  }
133
153
 
134
154
  /**
@@ -154,7 +174,7 @@ class CorruptedText {
154
174
  }
155
175
 
156
176
  // Continue animation to next variant
157
- setTimeout(() => {
177
+ this._animateTimeoutId = setTimeout(() => {
158
178
  if (this.isAnimating) {
159
179
  this.animate();
160
180
  }
@@ -214,7 +234,7 @@ class CorruptedText {
214
234
 
215
235
  // Schedule next corruption step
216
236
  this.animationFrame = requestAnimationFrame(() => {
217
- setTimeout(corrupt, this.options.cycleDelay);
237
+ this._corruptTimeoutId = setTimeout(corrupt, this.options.cycleDelay);
218
238
  });
219
239
  };
220
240
 
@@ -0,0 +1,46 @@
1
+ /**
2
+ * EventTracker - Tracks addEventListener calls for bulk removal.
3
+ *
4
+ * Stores references to all attached listeners so they can be removed
5
+ * in a single removeAll() call during component destroy().
6
+ *
7
+ * Usage:
8
+ * const events = new EventTracker();
9
+ * events.add(button, 'click', handler);
10
+ * events.add(window, 'resize', onResize, { passive: true });
11
+ * events.removeAll(); // removes everything
12
+ *
13
+ * @module core/event-tracker
14
+ */
15
+
16
+ export class EventTracker {
17
+ constructor() {
18
+ /** @type {Array<{target: EventTarget, event: string, handler: Function, options: any}>} */
19
+ this._listeners = [];
20
+ }
21
+
22
+ /**
23
+ * Attach an event listener and track it for later removal.
24
+ * @param {EventTarget} target - DOM element, window, or document
25
+ * @param {string} event - Event name (e.g. 'click', 'keydown')
26
+ * @param {Function} handler - Event handler function
27
+ * @param {Object|boolean} [options] - addEventListener options
28
+ */
29
+ add(target, event, handler, options) {
30
+ target.addEventListener(event, handler, options);
31
+ this._listeners.push({ target, event, handler, options });
32
+ }
33
+
34
+ /** Remove all tracked listeners. Call this in destroy(). */
35
+ removeAll() {
36
+ for (const { target, event, handler, options } of this._listeners) {
37
+ target.removeEventListener(event, handler, options);
38
+ }
39
+ this._listeners = [];
40
+ }
41
+
42
+ /** @returns {number} count of tracked listeners */
43
+ get count() {
44
+ return this._listeners.length;
45
+ }
46
+ }
@@ -0,0 +1,94 @@
1
+ /**
2
+ * TimerRegistry - Centralized timer tracking for component lifecycle cleanup.
3
+ *
4
+ * Wraps setTimeout, setInterval, and requestAnimationFrame so that all
5
+ * pending async work can be cancelled in a single clearAll() call.
6
+ *
7
+ * Usage:
8
+ * const timers = new TimerRegistry();
9
+ * timers.setTimeout(() => { ... }, 1000);
10
+ * timers.setInterval(() => { ... }, 500);
11
+ * timers.requestAnimationFrame((ts) => { ... });
12
+ * timers.clearAll(); // cancels everything
13
+ *
14
+ * @module core/timer-registry
15
+ */
16
+
17
+ export class TimerRegistry {
18
+ constructor() {
19
+ this._timeouts = new Set();
20
+ this._intervals = new Set();
21
+ this._rafs = new Set();
22
+ }
23
+
24
+ /**
25
+ * Tracked setTimeout — auto-removes ID after callback fires.
26
+ * @param {Function} fn
27
+ * @param {number} delay
28
+ * @returns {number} timeout ID
29
+ */
30
+ setTimeout(fn, delay) {
31
+ const id = setTimeout(() => {
32
+ this._timeouts.delete(id);
33
+ fn();
34
+ }, delay);
35
+ this._timeouts.add(id);
36
+ return id;
37
+ }
38
+
39
+ /**
40
+ * Tracked setInterval.
41
+ * @param {Function} fn
42
+ * @param {number} delay
43
+ * @returns {number} interval ID
44
+ */
45
+ setInterval(fn, delay) {
46
+ const id = setInterval(fn, delay);
47
+ this._intervals.add(id);
48
+ return id;
49
+ }
50
+
51
+ /**
52
+ * Tracked requestAnimationFrame — auto-removes ID after callback fires.
53
+ * @param {Function} fn
54
+ * @returns {number} RAF ID
55
+ */
56
+ requestAnimationFrame(fn) {
57
+ const id = requestAnimationFrame((ts) => {
58
+ this._rafs.delete(id);
59
+ fn(ts);
60
+ });
61
+ this._rafs.add(id);
62
+ return id;
63
+ }
64
+
65
+ clearTimeout(id) {
66
+ clearTimeout(id);
67
+ this._timeouts.delete(id);
68
+ }
69
+
70
+ clearInterval(id) {
71
+ clearInterval(id);
72
+ this._intervals.delete(id);
73
+ }
74
+
75
+ cancelAnimationFrame(id) {
76
+ cancelAnimationFrame(id);
77
+ this._rafs.delete(id);
78
+ }
79
+
80
+ /** Cancel ALL tracked timers. Call this in destroy(). */
81
+ clearAll() {
82
+ this._timeouts.forEach(id => clearTimeout(id));
83
+ this._intervals.forEach(id => clearInterval(id));
84
+ this._rafs.forEach(id => cancelAnimationFrame(id));
85
+ this._timeouts.clear();
86
+ this._intervals.clear();
87
+ this._rafs.clear();
88
+ }
89
+
90
+ /** @returns {number} count of pending timers */
91
+ get pendingCount() {
92
+ return this._timeouts.size + this._intervals.size + this._rafs.size;
93
+ }
94
+ }