tiny-essentials 1.24.5 → 1.25.1

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.
Files changed (37) hide show
  1. package/README.md +8 -8
  2. package/changelog/1/25/0.md +13 -0
  3. package/changelog/1/25/1.md +18 -0
  4. package/dist/v1/TinyAnalogClock.min.js +1 -0
  5. package/dist/v1/TinyBasicsEs.min.js +1 -1
  6. package/dist/v1/TinyEssentials.min.js +1 -1
  7. package/dist/v1/TinyTextDiffer.min.js +1 -0
  8. package/dist/v1/basics/html.cjs +151 -2
  9. package/dist/v1/basics/html.d.mts +109 -0
  10. package/dist/v1/basics/html.mjs +135 -2
  11. package/dist/v1/build/TinyAnalogClock.cjs +7 -0
  12. package/dist/v1/build/TinyAnalogClock.d.mts +3 -0
  13. package/dist/v1/build/TinyAnalogClock.mjs +2 -0
  14. package/dist/v1/build/TinyTextDiffer.cjs +7 -0
  15. package/dist/v1/build/TinyTextDiffer.d.mts +3 -0
  16. package/dist/v1/build/TinyTextDiffer.mjs +2 -0
  17. package/dist/v1/index.cjs +4 -0
  18. package/dist/v1/index.d.mts +3 -1
  19. package/dist/v1/index.mjs +3 -1
  20. package/dist/v1/libs/TinyAnalogClock.cjs +738 -0
  21. package/dist/v1/libs/TinyAnalogClock.d.mts +342 -0
  22. package/dist/v1/libs/TinyAnalogClock.mjs +653 -0
  23. package/dist/v1/libs/TinyRateLimiter.cjs +215 -31
  24. package/dist/v1/libs/TinyRateLimiter.d.mts +179 -25
  25. package/dist/v1/libs/TinyRateLimiter.mjs +215 -31
  26. package/dist/v1/libs/TinyTextDiffer.cjs +288 -0
  27. package/dist/v1/libs/TinyTextDiffer.d.mts +109 -0
  28. package/dist/v1/libs/TinyTextDiffer.mjs +255 -0
  29. package/docs/v1/README.md +5 -8
  30. package/docs/v1/basics/html.md +69 -0
  31. package/docs/v1/libs/TinyAnalogClock.md +295 -0
  32. package/docs/v1/libs/TinyRateLimiter.md +11 -0
  33. package/docs/v1/libs/TinyTextDiffer.md +114 -0
  34. package/package.json +12 -4
  35. package/docs/v1/Ai-Tips.md +0 -51
  36. package/docs/v1/Personal-Ai-Prompts(portuguese).md +0 -134
  37. package/docs/v1/Personal-Ai-Prompts.md +0 -113
@@ -0,0 +1 @@
1
+ (()=>{"use strict";var e={d:(t,r)=>{for(var s in r)e.o(r,s)&&!e.o(t,s)&&Object.defineProperty(t,s,{enumerable:!0,get:r[s]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)},t={};e.d(t,{TinyTextDiffer:()=>r});const r=class{#e=[];#t=!1;get history(){return[...this.#e]}set history(e){if(!Array.isArray(e))throw new TypeError("History data must be provided as an array.");if(!e.every(e=>"string"==typeof e))throw new TypeError("All items in the history array must be strings.");this.#e=[...e]}get size(){return this.#e.length}constructor(e=[]){this.history=e}#r(){if(this.#t)throw new Error("Cannot perform operations on a destroyed TinyTextDiffer instance.")}#s(e){if("number"!=typeof e)throw new TypeError("The provided index must be a valid number.")}#i(e){if("string"!=typeof e)throw new TypeError("The provided text must be a valid string.")}get(e){if(this.#r(),this.#s(e),void 0===this.#e[e])throw new Error(`No text version found at index ${e}.`);return this.#e[e]}has(e){return this.#r(),this.#s(e),void 0!==this.#e[e]}add(e){this.#r(),this.#i(e),this.#e.push(e)}remove(){return this.#r(),this.#e.pop()}addAt(e,t){this.#r(),this.#s(e),this.#i(t),this.#e.splice(e,0,t)}removeAt(e){this.#r(),this.#s(e);const t=this.#e.length;return this.#e.splice(e,1),t!==this.#e.length}clear(){this.#r(),this.#e=[]}compare(...e){this.#r();const t=e.length;if(0===t||t%2!=0)throw new Error("The compare method requires an even number of indices to form comparison pairs.");const r=[];for(let s=0;s<t;s+=2){const i=s+1;if(i>t-1)continue;this.#s(e[s]),this.#s(e[i]);const h=this.#e[e[s]],o=this.#e[e[i]];r.push(this._computeDiff(h,o))}return r}_computeDiff(e,t){const r=e.length,s=t.length,i=Array(r+1).fill(null).map(()=>Array(s+1).fill(0));let h=1,o=1;for(h=1;h<=r;h++)for(o=1;o<=s;o++)e[h-1]===t[o-1]?i[h][o]=i[h-1][o-1]+1:i[h][o]=Math.max(i[h-1][o],i[h][o-1]);h=r,o=s;const n=[];for(;h>0||o>0;)h>0&&o>0&&e[h-1]===t[o-1]?(n.unshift({value:e[h-1],type:"normal"}),h--,o--):o>0&&(0===h||i[h][o-1]>=i[h-1][o])?(n.unshift({value:t[o-1],type:"added"}),o--):h>0&&(0===o||i[h][o-1]<i[h-1][o])&&(n.unshift({value:e[h-1],type:"deleted"}),h--);return n.reduce((e,t)=>{const r=e.length-1;return e.length>0&&e[r].type===t.type?e[r].value+=t.value:e.push(t),e},[])}destroy(){this.#t||(this.#e=[],this.#t=!0)}};window.TinyTextDiffer=t.TinyTextDiffer})();
@@ -134,8 +134,60 @@ function saveJsonFile(filename, data, spaces = 2) {
134
134
  * @property {number} [retries=0] - Number of retry attempts (ignored if signal is provided).
135
135
  * @property {Headers|Record<string, *>} [headers={}] - Additional headers.
136
136
  * @property {AbortSignal|null} [signal] - External AbortSignal; disables timeout and retries.
137
+ * @property {FetchOnProgressResult} [onProgress] - Track the load progress.
137
138
  */
138
139
 
140
+ /**
141
+ * Callback function used to report the progress of a data download.
142
+ *
143
+ * @callback FetchOnProgressResult
144
+ * @param {number} loaded - The amount of bytes currently loaded.
145
+ * @param {number} total - The total amount of bytes to be loaded (0 if unknown).
146
+ * @returns {void}
147
+ */
148
+
149
+ /**
150
+ * Intercepts a standard Fetch API Response to track the download progress
151
+ * of its body stream.
152
+ *
153
+ * This function returns a new Response object with a monitored ReadableStream,
154
+ * allowing the provided callback to receive updates on the number of bytes loaded.
155
+ *
156
+ * @param {Response} response - The original response object to be tracked.
157
+ * @param {FetchOnProgressResult} onProgress - The callback function to handle progress events.
158
+ * @returns {Response} A new Response object with the tracked stream.
159
+ */
160
+ function trackFetchProgress(response, onProgress) {
161
+ if (typeof onProgress !== 'function')
162
+ throw new TypeError('The "onProgress" argument must be a function.');
163
+ if (!response.body) return response;
164
+
165
+ // Handle Progress Tracking via ReadableStream
166
+ const contentLength = response.headers.get('content-length');
167
+ const total = contentLength ? parseInt(contentLength, 10) : 0;
168
+ let loaded = 0;
169
+
170
+ const reader = response.body.getReader();
171
+ const stream = new ReadableStream({
172
+ async start(controller) {
173
+ while (true) {
174
+ const { done, value } = await reader.read();
175
+ if (done) break;
176
+ loaded += value.byteLength;
177
+ onProgress(loaded, total);
178
+ controller.enqueue(value);
179
+ }
180
+ controller.close();
181
+ },
182
+ });
183
+
184
+ return new Response(stream, {
185
+ headers: response.headers,
186
+ status: response.status,
187
+ statusText: response.statusText,
188
+ });
189
+ }
190
+
139
191
  /**
140
192
  * @param {string} url - The full URL to fetch data from.
141
193
  * @param {FetchTemplateOptions} [options] - Optional settings.
@@ -144,7 +196,7 @@ function saveJsonFile(filename, data, spaces = 2) {
144
196
  */
145
197
  async function fetchTemplate(
146
198
  url,
147
- { method = 'GET', body, timeout = 0, retries = 0, headers = {}, signal = null } = {},
199
+ { method = 'GET', body, timeout = 0, retries = 0, headers = {}, signal = null, onProgress } = {},
148
200
  ) {
149
201
  if (
150
202
  typeof url !== 'string' ||
@@ -201,7 +253,10 @@ async function fetchTemplate(
201
253
  if (timer) clearTimeout(timer);
202
254
 
203
255
  if (!response.ok) throw new Error(`HTTP error: ${response.status} ${response.statusText}`);
204
- return response;
256
+
257
+ // If onProgress is not provided or body is null, return original response
258
+ if (!onProgress) return response;
259
+ return trackFetchProgress(response, onProgress);
205
260
  } catch (err) {
206
261
  lastError = /** @type {Error} */ (err);
207
262
  if (signal) break; // if an external signal came, it does not retry
@@ -304,6 +359,98 @@ async function fetchText(url, allowedMimeTypes, options) {
304
359
 
305
360
  ///////////////////////////////////////////////////////////////////////////////
306
361
 
362
+ /**
363
+ * Represents the final state and metadata of an attempted image load.
364
+ * @typedef {Object} ImageLoadResult
365
+ * @property {HTMLImageElement} element The image element instance used for loading.
366
+ * @property {Event} event The browser event triggered by the final lifecycle state.
367
+ * @property {ImageLoadStatus} status The specific outcome string of the loading process.
368
+ * @property {boolean} isSuccess Indicates if the image was successfully loaded without errors.
369
+ * @property {number} loadTimeMs The total duration in milliseconds from start to finish.
370
+ * @property {Object} dimensions An object containing the rendered and intrinsic sizes of the image.
371
+ * @property {number} dimensions.width The current layout width of the image element.
372
+ * @property {number} dimensions.height The current layout height of the image element.
373
+ * @property {number} dimensions.naturalWidth The intrinsic width of the image source in pixels.
374
+ * @property {number} dimensions.naturalHeight The intrinsic height of the image source in pixels.
375
+ */
376
+
377
+ /**
378
+ * Describes the possible resolution states for the image loading attempt.
379
+ * @typedef {'loaded'|'aborted'} ImageLoadStatus
380
+ */
381
+
382
+ /**
383
+ * Loads an image asynchronously, capturing critical lifecycle events.
384
+ * It normalizes errors and success states into a consistent result structure.
385
+ *
386
+ * @param {Object} options
387
+ * @param {string} options.url
388
+ * @param {string} [options.crossOrigin="anonymous"]
389
+ * @param {(event: Event, startTime: number) => void} [options.onLoading]
390
+ * @returns {Promise<ImageLoadResult>}
391
+ */
392
+ async function loadImage({ url, onLoading, crossOrigin = 'anonymous' }) {
393
+ return new Promise((resolve, reject) => {
394
+ const img = new Image();
395
+ let startTime = performance.now();
396
+
397
+ img.crossOrigin = crossOrigin;
398
+
399
+ /**
400
+ * Cleans up event listeners to avoid memory leaks.
401
+ */
402
+ const cleanup = () => {
403
+ img.onload = null;
404
+ img.onerror = null;
405
+ img.onabort = null;
406
+ img.onloadstart = null;
407
+ };
408
+
409
+ /**
410
+ * Centralized handler to generate the result object.
411
+ * @param {Event} event
412
+ * @param {ImageLoadStatus} status
413
+ * @param {boolean} isSuccess
414
+ */
415
+ const handleResult = (event, status, isSuccess) => {
416
+ const endTime = performance.now();
417
+ cleanup();
418
+
419
+ const result = {
420
+ element: img,
421
+ isSuccess,
422
+ event,
423
+ status,
424
+ loadTimeMs: endTime - startTime,
425
+ dimensions: {
426
+ width: img.width,
427
+ height: img.height,
428
+ naturalWidth: img.naturalWidth,
429
+ naturalHeight: img.naturalHeight,
430
+ },
431
+ };
432
+
433
+ resolve(result);
434
+ };
435
+
436
+ // Fired when the browser starts looking for the image data.
437
+ // Crucial for resetting the timer to network start, not just script start.
438
+ img.onloadstart = (ev) => {
439
+ startTime = performance.now();
440
+ if (typeof onLoading === 'function') onLoading(ev, startTime);
441
+ };
442
+
443
+ img.onload = (ev) => handleResult(ev, 'loaded', true);
444
+ img.onabort = (ev) => handleResult(ev, 'aborted', false);
445
+ img.onerror = (ev) => reject(ev);
446
+
447
+ // Trigger the load
448
+ img.src = url;
449
+ });
450
+ }
451
+
452
+ ///////////////////////////////////////////////////////////////////////////////
453
+
307
454
  /**
308
455
  * Installs a script that toggles CSS classes on a given element
309
456
  * based on the page's visibility or focus state, and optionally
@@ -438,7 +585,9 @@ exports.fetchBlob = fetchBlob;
438
585
  exports.fetchJson = fetchJson;
439
586
  exports.fetchText = fetchText;
440
587
  exports.installWindowHiddenScript = installWindowHiddenScript;
588
+ exports.loadImage = loadImage;
441
589
  exports.readBase64Blob = readBase64Blob;
442
590
  exports.readFileBlob = readFileBlob;
443
591
  exports.readJsonBlob = readJsonBlob;
444
592
  exports.saveJsonFile = saveJsonFile;
593
+ exports.trackFetchProgress = trackFetchProgress;
@@ -45,6 +45,36 @@ export function readJsonBlob(file: File): Promise<Record<string | number | symbo
45
45
  * @param {number} [spaces=2]
46
46
  */
47
47
  export function saveJsonFile(filename: string, data: any, spaces?: number): void;
48
+ /**
49
+ * @typedef {Object} FetchTemplateOptions
50
+ * @property {string} [method="GET"] - HTTP method to use (GET, POST, etc.).
51
+ * @property {any} [body] - Request body (only for methods like POST, PUT).
52
+ * @property {number} [timeout=0] - Timeout in milliseconds (ignored if signal is provided).
53
+ * @property {number} [retries=0] - Number of retry attempts (ignored if signal is provided).
54
+ * @property {Headers|Record<string, *>} [headers={}] - Additional headers.
55
+ * @property {AbortSignal|null} [signal] - External AbortSignal; disables timeout and retries.
56
+ * @property {FetchOnProgressResult} [onProgress] - Track the load progress.
57
+ */
58
+ /**
59
+ * Callback function used to report the progress of a data download.
60
+ *
61
+ * @callback FetchOnProgressResult
62
+ * @param {number} loaded - The amount of bytes currently loaded.
63
+ * @param {number} total - The total amount of bytes to be loaded (0 if unknown).
64
+ * @returns {void}
65
+ */
66
+ /**
67
+ * Intercepts a standard Fetch API Response to track the download progress
68
+ * of its body stream.
69
+ *
70
+ * This function returns a new Response object with a monitored ReadableStream,
71
+ * allowing the provided callback to receive updates on the number of bytes loaded.
72
+ *
73
+ * @param {Response} response - The original response object to be tracked.
74
+ * @param {FetchOnProgressResult} onProgress - The callback function to handle progress events.
75
+ * @returns {Response} A new Response object with the tracked stream.
76
+ */
77
+ export function trackFetchProgress(response: Response, onProgress: FetchOnProgressResult): Response;
48
78
  /**
49
79
  * Loads and parses a JSON from a remote URL using Fetch API.
50
80
  *
@@ -74,6 +104,39 @@ export function fetchBlob(url: string, allowedMimeTypes?: string[], options?: Fe
74
104
  * @throws {Error} Throws if fetch fails, response is not ok, or MIME type is not allowed.
75
105
  */
76
106
  export function fetchText(url: string, allowedMimeTypes?: string[], options?: FetchTemplateOptions): Promise<string>;
107
+ /**
108
+ * Represents the final state and metadata of an attempted image load.
109
+ * @typedef {Object} ImageLoadResult
110
+ * @property {HTMLImageElement} element The image element instance used for loading.
111
+ * @property {Event} event The browser event triggered by the final lifecycle state.
112
+ * @property {ImageLoadStatus} status The specific outcome string of the loading process.
113
+ * @property {boolean} isSuccess Indicates if the image was successfully loaded without errors.
114
+ * @property {number} loadTimeMs The total duration in milliseconds from start to finish.
115
+ * @property {Object} dimensions An object containing the rendered and intrinsic sizes of the image.
116
+ * @property {number} dimensions.width The current layout width of the image element.
117
+ * @property {number} dimensions.height The current layout height of the image element.
118
+ * @property {number} dimensions.naturalWidth The intrinsic width of the image source in pixels.
119
+ * @property {number} dimensions.naturalHeight The intrinsic height of the image source in pixels.
120
+ */
121
+ /**
122
+ * Describes the possible resolution states for the image loading attempt.
123
+ * @typedef {'loaded'|'aborted'} ImageLoadStatus
124
+ */
125
+ /**
126
+ * Loads an image asynchronously, capturing critical lifecycle events.
127
+ * It normalizes errors and success states into a consistent result structure.
128
+ *
129
+ * @param {Object} options
130
+ * @param {string} options.url
131
+ * @param {string} [options.crossOrigin="anonymous"]
132
+ * @param {(event: Event, startTime: number) => void} [options.onLoading]
133
+ * @returns {Promise<ImageLoadResult>}
134
+ */
135
+ export function loadImage({ url, onLoading, crossOrigin }: {
136
+ url: string;
137
+ crossOrigin?: string | undefined;
138
+ onLoading?: ((event: Event, startTime: number) => void) | undefined;
139
+ }): Promise<ImageLoadResult>;
77
140
  /**
78
141
  * Installs a script that toggles CSS classes on a given element
79
142
  * based on the page's visibility or focus state, and optionally
@@ -128,5 +191,51 @@ export type FetchTemplateOptions = {
128
191
  * - External AbortSignal; disables timeout and retries.
129
192
  */
130
193
  signal?: AbortSignal | null | undefined;
194
+ /**
195
+ * - Track the load progress.
196
+ */
197
+ onProgress?: FetchOnProgressResult | undefined;
131
198
  };
199
+ /**
200
+ * Callback function used to report the progress of a data download.
201
+ */
202
+ export type FetchOnProgressResult = (loaded: number, total: number) => void;
203
+ /**
204
+ * Represents the final state and metadata of an attempted image load.
205
+ */
206
+ export type ImageLoadResult = {
207
+ /**
208
+ * The image element instance used for loading.
209
+ */
210
+ element: HTMLImageElement;
211
+ /**
212
+ * The browser event triggered by the final lifecycle state.
213
+ */
214
+ event: Event;
215
+ /**
216
+ * The specific outcome string of the loading process.
217
+ */
218
+ status: ImageLoadStatus;
219
+ /**
220
+ * Indicates if the image was successfully loaded without errors.
221
+ */
222
+ isSuccess: boolean;
223
+ /**
224
+ * The total duration in milliseconds from start to finish.
225
+ */
226
+ loadTimeMs: number;
227
+ /**
228
+ * An object containing the rendered and intrinsic sizes of the image.
229
+ */
230
+ dimensions: {
231
+ width: number;
232
+ height: number;
233
+ naturalWidth: number;
234
+ naturalHeight: number;
235
+ };
236
+ };
237
+ /**
238
+ * Describes the possible resolution states for the image loading attempt.
239
+ */
240
+ export type ImageLoadStatus = "loaded" | "aborted";
132
241
  //# sourceMappingURL=html.d.mts.map
@@ -122,14 +122,63 @@ export function saveJsonFile(filename, data, spaces = 2) {
122
122
  * @property {number} [retries=0] - Number of retry attempts (ignored if signal is provided).
123
123
  * @property {Headers|Record<string, *>} [headers={}] - Additional headers.
124
124
  * @property {AbortSignal|null} [signal] - External AbortSignal; disables timeout and retries.
125
+ * @property {FetchOnProgressResult} [onProgress] - Track the load progress.
125
126
  */
127
+ /**
128
+ * Callback function used to report the progress of a data download.
129
+ *
130
+ * @callback FetchOnProgressResult
131
+ * @param {number} loaded - The amount of bytes currently loaded.
132
+ * @param {number} total - The total amount of bytes to be loaded (0 if unknown).
133
+ * @returns {void}
134
+ */
135
+ /**
136
+ * Intercepts a standard Fetch API Response to track the download progress
137
+ * of its body stream.
138
+ *
139
+ * This function returns a new Response object with a monitored ReadableStream,
140
+ * allowing the provided callback to receive updates on the number of bytes loaded.
141
+ *
142
+ * @param {Response} response - The original response object to be tracked.
143
+ * @param {FetchOnProgressResult} onProgress - The callback function to handle progress events.
144
+ * @returns {Response} A new Response object with the tracked stream.
145
+ */
146
+ export function trackFetchProgress(response, onProgress) {
147
+ if (typeof onProgress !== 'function')
148
+ throw new TypeError('The "onProgress" argument must be a function.');
149
+ if (!response.body)
150
+ return response;
151
+ // Handle Progress Tracking via ReadableStream
152
+ const contentLength = response.headers.get('content-length');
153
+ const total = contentLength ? parseInt(contentLength, 10) : 0;
154
+ let loaded = 0;
155
+ const reader = response.body.getReader();
156
+ const stream = new ReadableStream({
157
+ async start(controller) {
158
+ while (true) {
159
+ const { done, value } = await reader.read();
160
+ if (done)
161
+ break;
162
+ loaded += value.byteLength;
163
+ onProgress(loaded, total);
164
+ controller.enqueue(value);
165
+ }
166
+ controller.close();
167
+ },
168
+ });
169
+ return new Response(stream, {
170
+ headers: response.headers,
171
+ status: response.status,
172
+ statusText: response.statusText,
173
+ });
174
+ }
126
175
  /**
127
176
  * @param {string} url - The full URL to fetch data from.
128
177
  * @param {FetchTemplateOptions} [options] - Optional settings.
129
178
  * @returns {Promise<Response>} Result data.
130
179
  * @throws {Error} Throws if fetch fails, times out.
131
180
  */
132
- async function fetchTemplate(url, { method = 'GET', body, timeout = 0, retries = 0, headers = {}, signal = null } = {}) {
181
+ async function fetchTemplate(url, { method = 'GET', body, timeout = 0, retries = 0, headers = {}, signal = null, onProgress } = {}) {
133
182
  if (typeof url !== 'string' ||
134
183
  (!url.startsWith('../') &&
135
184
  !url.startsWith('./') &&
@@ -172,7 +221,10 @@ async function fetchTemplate(url, { method = 'GET', body, timeout = 0, retries =
172
221
  clearTimeout(timer);
173
222
  if (!response.ok)
174
223
  throw new Error(`HTTP error: ${response.status} ${response.statusText}`);
175
- return response;
224
+ // If onProgress is not provided or body is null, return original response
225
+ if (!onProgress)
226
+ return response;
227
+ return trackFetchProgress(response, onProgress);
176
228
  }
177
229
  catch (err) {
178
230
  lastError = /** @type {Error} */ (err);
@@ -258,6 +310,87 @@ export async function fetchText(url, allowedMimeTypes, options) {
258
310
  });
259
311
  }
260
312
  ///////////////////////////////////////////////////////////////////////////////
313
+ /**
314
+ * Represents the final state and metadata of an attempted image load.
315
+ * @typedef {Object} ImageLoadResult
316
+ * @property {HTMLImageElement} element The image element instance used for loading.
317
+ * @property {Event} event The browser event triggered by the final lifecycle state.
318
+ * @property {ImageLoadStatus} status The specific outcome string of the loading process.
319
+ * @property {boolean} isSuccess Indicates if the image was successfully loaded without errors.
320
+ * @property {number} loadTimeMs The total duration in milliseconds from start to finish.
321
+ * @property {Object} dimensions An object containing the rendered and intrinsic sizes of the image.
322
+ * @property {number} dimensions.width The current layout width of the image element.
323
+ * @property {number} dimensions.height The current layout height of the image element.
324
+ * @property {number} dimensions.naturalWidth The intrinsic width of the image source in pixels.
325
+ * @property {number} dimensions.naturalHeight The intrinsic height of the image source in pixels.
326
+ */
327
+ /**
328
+ * Describes the possible resolution states for the image loading attempt.
329
+ * @typedef {'loaded'|'aborted'} ImageLoadStatus
330
+ */
331
+ /**
332
+ * Loads an image asynchronously, capturing critical lifecycle events.
333
+ * It normalizes errors and success states into a consistent result structure.
334
+ *
335
+ * @param {Object} options
336
+ * @param {string} options.url
337
+ * @param {string} [options.crossOrigin="anonymous"]
338
+ * @param {(event: Event, startTime: number) => void} [options.onLoading]
339
+ * @returns {Promise<ImageLoadResult>}
340
+ */
341
+ export async function loadImage({ url, onLoading, crossOrigin = 'anonymous' }) {
342
+ return new Promise((resolve, reject) => {
343
+ const img = new Image();
344
+ let startTime = performance.now();
345
+ img.crossOrigin = crossOrigin;
346
+ /**
347
+ * Cleans up event listeners to avoid memory leaks.
348
+ */
349
+ const cleanup = () => {
350
+ img.onload = null;
351
+ img.onerror = null;
352
+ img.onabort = null;
353
+ img.onloadstart = null;
354
+ };
355
+ /**
356
+ * Centralized handler to generate the result object.
357
+ * @param {Event} event
358
+ * @param {ImageLoadStatus} status
359
+ * @param {boolean} isSuccess
360
+ */
361
+ const handleResult = (event, status, isSuccess) => {
362
+ const endTime = performance.now();
363
+ cleanup();
364
+ const result = {
365
+ element: img,
366
+ isSuccess,
367
+ event,
368
+ status,
369
+ loadTimeMs: endTime - startTime,
370
+ dimensions: {
371
+ width: img.width,
372
+ height: img.height,
373
+ naturalWidth: img.naturalWidth,
374
+ naturalHeight: img.naturalHeight,
375
+ },
376
+ };
377
+ resolve(result);
378
+ };
379
+ // Fired when the browser starts looking for the image data.
380
+ // Crucial for resetting the timer to network start, not just script start.
381
+ img.onloadstart = (ev) => {
382
+ startTime = performance.now();
383
+ if (typeof onLoading === 'function')
384
+ onLoading(ev, startTime);
385
+ };
386
+ img.onload = (ev) => handleResult(ev, 'loaded', true);
387
+ img.onabort = (ev) => handleResult(ev, 'aborted', false);
388
+ img.onerror = (ev) => reject(ev);
389
+ // Trigger the load
390
+ img.src = url;
391
+ });
392
+ }
393
+ ///////////////////////////////////////////////////////////////////////////////
261
394
  /**
262
395
  * Installs a script that toggles CSS classes on a given element
263
396
  * based on the page's visibility or focus state, and optionally
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ var TinyAnalogClock = require('../libs/TinyAnalogClock.cjs');
4
+
5
+
6
+
7
+ exports.TinyAnalogClock = TinyAnalogClock;
@@ -0,0 +1,3 @@
1
+ export { TinyAnalogClock };
2
+ import TinyAnalogClock from '../libs/TinyAnalogClock.mjs';
3
+ //# sourceMappingURL=TinyAnalogClock.d.mts.map
@@ -0,0 +1,2 @@
1
+ import TinyAnalogClock from '../libs/TinyAnalogClock.mjs';
2
+ export { TinyAnalogClock };
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ var TinyTextDiffer = require('../libs/TinyTextDiffer.cjs');
4
+
5
+
6
+
7
+ exports.TinyTextDiffer = TinyTextDiffer;
@@ -0,0 +1,3 @@
1
+ export { TinyTextDiffer };
2
+ import TinyTextDiffer from '../libs/TinyTextDiffer.mjs';
3
+ //# sourceMappingURL=TinyTextDiffer.d.mts.map
@@ -0,0 +1,2 @@
1
+ import TinyTextDiffer from '../libs/TinyTextDiffer.mjs';
2
+ export { TinyTextDiffer };
package/dist/v1/index.cjs CHANGED
@@ -47,6 +47,8 @@ var TinySimpleDice = require('./libs/TinySimpleDice.cjs');
47
47
  var TinyElementObserver = require('./libs/TinyElementObserver.cjs');
48
48
  var TinyLoadingScreen = require('./libs/TinyLoadingScreen.cjs');
49
49
  var TinyColorValidator = require('./libs/TinyColorValidator.cjs');
50
+ var TinyAnalogClock = require('./libs/TinyAnalogClock.cjs');
51
+ var TinyTextDiffer = require('./libs/TinyTextDiffer.cjs');
50
52
 
51
53
 
52
54
 
@@ -175,3 +177,5 @@ exports.TinySimpleDice = TinySimpleDice;
175
177
  exports.TinyElementObserver = TinyElementObserver;
176
178
  exports.TinyLoadingScreen = TinyLoadingScreen;
177
179
  exports.TinyColorValidator = TinyColorValidator;
180
+ exports.TinyAnalogClock = TinyAnalogClock;
181
+ exports.TinyTextDiffer = TinyTextDiffer;
@@ -1,3 +1,5 @@
1
+ import TinyTextDiffer from './libs/TinyTextDiffer.mjs';
2
+ import TinyAnalogClock from './libs/TinyAnalogClock.mjs';
1
3
  import TinyColorValidator from './libs/TinyColorValidator.mjs';
2
4
  import TinyLoadingScreen from './libs/TinyLoadingScreen.mjs';
3
5
  import TinyElementObserver from './libs/TinyElementObserver.mjs';
@@ -123,5 +125,5 @@ import { getTimeDuration } from './basics/clock.mjs';
123
125
  import { shuffleArray } from './basics/array.mjs';
124
126
  import { toTitleCase } from './basics/text.mjs';
125
127
  import { toTitleCaseLowerFirst } from './basics/text.mjs';
126
- export { TinyColorValidator, TinyLoadingScreen, TinyElementObserver, TinySimpleDice, TinyNeedBar, TinyI18, TinyCookieConsent, TinyInventory, TinyInventoryTrader, TinyArrayPaginator, TinyAdvancedRaffle, TinyDayNightCycle, TinyGamepad, TinyTextarea, TinyNewWinEvents, TinyIframeEvents, TinyLocalStorage, TinyEvents, TinyTimeout, TinyColorConverter, TinyClipboard, TinyTextRangeEditor, TinySmartScroller, UltraRandomMsgGen, TinyAfterScrollWatcher, TinyHtml, TinyNotifications, TinyDomReadyManager, TinyDragger, TinyDragDropDetector, TinyToastNotify, TinyNotifyCenter, TinyRateLimiter, ColorSafeStringify, TinyPromiseQueue, TinyLevelUp, diffArrayList, diffStrings, breakdownDuration, calculateMarketcap, compareMarketcap, getPercentage, areElsCollTop, areElsCollBottom, areElsCollLeft, areElsCollRight, areElsCollPerfTop, areElsCollPerfBottom, areElsCollPerfLeft, areElsCollPerfRight, areElsColliding, areElsPerfColliding, getElsColliding, getElsPerfColliding, getElsCollOverlap, getElsCollOverlapPos, getRectCenter, getElsRelativeCenterOffset, getElsCollDirDepth, getElsCollDetails, safeTextTrim, installWindowHiddenScript, genFibonacciSeq, isDirEmptyAsync, fileSizeAsync, dirSizeAsync, listFilesAsync, listDirsAsync, getLatestBackupPath, fetchJson, fetchText, readJsonBlob, readFileBlob, readBase64Blob, saveJsonFile, readJsonFile, writeJsonFile, ensureDirectory, clearDirectoryAsync, clearDirectory, fileExists, dirExists, isDirEmpty, ensureCopyFile, tryDeleteFile, writeTextFile, listFiles, listDirs, fileSize, dirSize, backupFile, restoreLatestBackup, renameFileBatch, renameFileRegex, renameFileAddPrefixSuffix, renameFileNormalizeCase, renameFilePadNumbers, documentIsFullScreen, isScreenFilled, requestFullScreen, exitFullScreen, isFullScreenMode, onFullScreenChange, offFullScreenChange, isJsonObject, arraySortPositions, formatBytes, addAiMarkerShortcut, extendObjType, reorderObjTypeOrder, cloneObjTypeOrder, countObj, checkObj, objType, ruleOfThree, getSimplePerc, asyncReplace, getAge, formatCustomTimer, formatDayTimer, formatTimer, getTimeDuration, shuffleArray, toTitleCase, toTitleCaseLowerFirst };
128
+ export { TinyTextDiffer, TinyAnalogClock, TinyColorValidator, TinyLoadingScreen, TinyElementObserver, TinySimpleDice, TinyNeedBar, TinyI18, TinyCookieConsent, TinyInventory, TinyInventoryTrader, TinyArrayPaginator, TinyAdvancedRaffle, TinyDayNightCycle, TinyGamepad, TinyTextarea, TinyNewWinEvents, TinyIframeEvents, TinyLocalStorage, TinyEvents, TinyTimeout, TinyColorConverter, TinyClipboard, TinyTextRangeEditor, TinySmartScroller, UltraRandomMsgGen, TinyAfterScrollWatcher, TinyHtml, TinyNotifications, TinyDomReadyManager, TinyDragger, TinyDragDropDetector, TinyToastNotify, TinyNotifyCenter, TinyRateLimiter, ColorSafeStringify, TinyPromiseQueue, TinyLevelUp, diffArrayList, diffStrings, breakdownDuration, calculateMarketcap, compareMarketcap, getPercentage, areElsCollTop, areElsCollBottom, areElsCollLeft, areElsCollRight, areElsCollPerfTop, areElsCollPerfBottom, areElsCollPerfLeft, areElsCollPerfRight, areElsColliding, areElsPerfColliding, getElsColliding, getElsPerfColliding, getElsCollOverlap, getElsCollOverlapPos, getRectCenter, getElsRelativeCenterOffset, getElsCollDirDepth, getElsCollDetails, safeTextTrim, installWindowHiddenScript, genFibonacciSeq, isDirEmptyAsync, fileSizeAsync, dirSizeAsync, listFilesAsync, listDirsAsync, getLatestBackupPath, fetchJson, fetchText, readJsonBlob, readFileBlob, readBase64Blob, saveJsonFile, readJsonFile, writeJsonFile, ensureDirectory, clearDirectoryAsync, clearDirectory, fileExists, dirExists, isDirEmpty, ensureCopyFile, tryDeleteFile, writeTextFile, listFiles, listDirs, fileSize, dirSize, backupFile, restoreLatestBackup, renameFileBatch, renameFileRegex, renameFileAddPrefixSuffix, renameFileNormalizeCase, renameFilePadNumbers, documentIsFullScreen, isScreenFilled, requestFullScreen, exitFullScreen, isFullScreenMode, onFullScreenChange, offFullScreenChange, isJsonObject, arraySortPositions, formatBytes, addAiMarkerShortcut, extendObjType, reorderObjTypeOrder, cloneObjTypeOrder, countObj, checkObj, objType, ruleOfThree, getSimplePerc, asyncReplace, getAge, formatCustomTimer, formatDayTimer, formatTimer, getTimeDuration, shuffleArray, toTitleCase, toTitleCaseLowerFirst };
127
129
  //# sourceMappingURL=index.d.mts.map
package/dist/v1/index.mjs CHANGED
@@ -45,7 +45,9 @@ import TinySimpleDice from './libs/TinySimpleDice.mjs';
45
45
  import TinyElementObserver from './libs/TinyElementObserver.mjs';
46
46
  import TinyLoadingScreen from './libs/TinyLoadingScreen.mjs';
47
47
  import TinyColorValidator from './libs/TinyColorValidator.mjs';
48
+ import TinyAnalogClock from './libs/TinyAnalogClock.mjs';
49
+ import TinyTextDiffer from './libs/TinyTextDiffer.mjs';
48
50
  // import TinyHtmlElems from './libs/TinyHtml/index.mjs';
49
- export {
51
+ export { TinyTextDiffer, TinyAnalogClock,
50
52
  // TinyHtmlElems,
51
53
  TinyColorValidator, TinyLoadingScreen, TinyElementObserver, TinySimpleDice, TinyNeedBar, TinyI18, TinyCookieConsent, TinyInventory, TinyInventoryTrader, TinyArrayPaginator, TinyAdvancedRaffle, TinyDayNightCycle, TinyGamepad, TinyTextarea, TinyNewWinEvents, TinyIframeEvents, TinyLocalStorage, TinyEvents, TinyTimeout, TinyColorConverter, TinyClipboard, TinyTextRangeEditor, TinySmartScroller, UltraRandomMsgGen, TinyAfterScrollWatcher, TinyHtml, TinyNotifications, TinyDomReadyManager, TinyDragger, TinyDragDropDetector, TinyToastNotify, TinyNotifyCenter, TinyRateLimiter, ColorSafeStringify, TinyPromiseQueue, TinyLevelUp, diffArrayList, diffStrings, breakdownDuration, calculateMarketcap, compareMarketcap, getPercentage, areElsCollTop, areElsCollBottom, areElsCollLeft, areElsCollRight, areElsCollPerfTop, areElsCollPerfBottom, areElsCollPerfLeft, areElsCollPerfRight, areElsColliding, areElsPerfColliding, getElsColliding, getElsPerfColliding, getElsCollOverlap, getElsCollOverlapPos, getRectCenter, getElsRelativeCenterOffset, getElsCollDirDepth, getElsCollDetails, safeTextTrim, installWindowHiddenScript, genFibonacciSeq, isDirEmptyAsync, fileSizeAsync, dirSizeAsync, listFilesAsync, listDirsAsync, getLatestBackupPath, fetchJson, fetchText, readJsonBlob, readFileBlob, readBase64Blob, saveJsonFile, readJsonFile, writeJsonFile, ensureDirectory, clearDirectoryAsync, clearDirectory, fileExists, dirExists, isDirEmpty, ensureCopyFile, tryDeleteFile, writeTextFile, listFiles, listDirs, fileSize, dirSize, backupFile, restoreLatestBackup, renameFileBatch, renameFileRegex, renameFileAddPrefixSuffix, renameFileNormalizeCase, renameFilePadNumbers, documentIsFullScreen, isScreenFilled, requestFullScreen, exitFullScreen, isFullScreenMode, onFullScreenChange, offFullScreenChange, isJsonObject, arraySortPositions, formatBytes, addAiMarkerShortcut, extendObjType, reorderObjTypeOrder, cloneObjTypeOrder, countObj, checkObj, objType, ruleOfThree, getSimplePerc, asyncReplace, getAge, formatCustomTimer, formatDayTimer, formatTimer, getTimeDuration, shuffleArray, toTitleCase, toTitleCaseLowerFirst, };