tiny-essentials 1.26.4 โ†’ 1.27.0

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 (120) hide show
  1. package/.github/workflows/node.js.yml +1 -1
  2. package/README.md +0 -1
  3. package/changelog/1/27/0.md +24 -0
  4. package/dist/v1/ColorSafeStringify.min.js +1 -1
  5. package/dist/v1/TinyAdvancedRaffle.min.js +1 -1
  6. package/dist/v1/TinyAfterScrollWatcher.min.js +1 -1
  7. package/dist/v1/TinyAnalogClock.min.js +1 -1
  8. package/dist/v1/TinyArrayComparator.min.js +1 -1
  9. package/dist/v1/TinyArrayPaginator.min.js +1 -1
  10. package/dist/v1/TinyBasicsEs.min.js +1 -1
  11. package/dist/v1/TinyClassManager.min.js +1 -1
  12. package/dist/v1/TinyClipboard.min.js +1 -1
  13. package/dist/v1/TinyColorConverter.min.js +1 -1
  14. package/dist/v1/TinyColorValidator.min.js +1 -1
  15. package/dist/v1/TinyCookieConsent.min.js +1 -1
  16. package/dist/v1/TinyDayNightCycle.min.js +1 -1
  17. package/dist/v1/TinyDomReadyManager.min.js +1 -1
  18. package/dist/v1/TinyDragDropDetector.min.js +1 -1
  19. package/dist/v1/TinyDragger.min.js +1 -1
  20. package/dist/v1/TinyElementObserver.min.js +1 -1
  21. package/dist/v1/TinyEssentials.min.js +1 -1
  22. package/dist/v1/TinyEvents.min.js +1 -1
  23. package/dist/v1/TinyGamepad.min.js +1 -1
  24. package/dist/v1/TinyHtml.min.js +1 -1
  25. package/dist/v1/TinyI18.min.js +1 -1
  26. package/dist/v1/TinyIframeEvents.min.js +1 -1
  27. package/dist/v1/TinyInventory.min.js +1 -1
  28. package/dist/v1/TinyInventoryTrader.min.js +1 -1
  29. package/dist/v1/TinyLevelUp.min.js +1 -1
  30. package/dist/v1/TinyLoadingScreen.min.js +1 -1
  31. package/dist/v1/TinyLocalStorage.min.js +1 -1
  32. package/dist/v1/TinyMaInSys.min.js +1 -1
  33. package/dist/v1/TinyMediaPlayer.min.js +1 -0
  34. package/dist/v1/TinyNeedBar.min.js +1 -1
  35. package/dist/v1/TinyNewWinEvents.min.js +1 -1
  36. package/dist/v1/TinyNotifications.min.js +1 -1
  37. package/dist/v1/TinyNotifyCenter.min.js +1 -1
  38. package/dist/v1/TinyPromiseQueue.min.js +1 -1
  39. package/dist/v1/TinyRadioFm.min.js +1 -0
  40. package/dist/v1/TinyRateLimiter.min.js +1 -1
  41. package/dist/v1/TinySimpleDice.min.js +1 -1
  42. package/dist/v1/TinySmartScroller.min.js +1 -1
  43. package/dist/v1/TinyTextDiffer.min.js +1 -1
  44. package/dist/v1/TinyTextRangeEditor.min.js +1 -1
  45. package/dist/v1/TinyTextarea.min.js +1 -1
  46. package/dist/v1/TinyTimeout.min.js +1 -1
  47. package/dist/v1/TinyToastNotify.min.js +1 -1
  48. package/dist/v1/TinyUploadClicker.min.js +1 -1
  49. package/dist/v1/UltraRandomMsgGen.min.js +1 -1
  50. package/dist/v1/basics/index.cjs +5 -0
  51. package/dist/v1/basics/index.d.mts +5 -1
  52. package/dist/v1/basics/index.mjs +2 -1
  53. package/dist/v1/basics/mediaContent.cjs +644 -0
  54. package/dist/v1/basics/mediaContent.d.mts +296 -0
  55. package/dist/v1/basics/mediaContent.mjs +557 -0
  56. package/dist/v1/build/TinyMediaPlayer.cjs +7 -0
  57. package/dist/v1/build/TinyMediaPlayer.d.mts +3 -0
  58. package/dist/v1/build/TinyMediaPlayer.mjs +2 -0
  59. package/dist/v1/build/TinyRadioFm.cjs +7 -0
  60. package/dist/v1/build/TinyRadioFm.d.mts +3 -0
  61. package/dist/v1/build/TinyRadioFm.mjs +2 -0
  62. package/dist/v1/index.cjs +9 -0
  63. package/dist/v1/index.d.mts +7 -1
  64. package/dist/v1/index.mjs +5 -2
  65. package/dist/v1/libs/ColorSafeStringify.d.mts +1 -1
  66. package/dist/v1/libs/TinyAfterScrollWatcher.cjs +16 -3
  67. package/dist/v1/libs/TinyAfterScrollWatcher.d.mts +7 -3
  68. package/dist/v1/libs/TinyAfterScrollWatcher.mjs +13 -3
  69. package/dist/v1/libs/TinyArrayComparator.cjs +14 -11
  70. package/dist/v1/libs/TinyArrayComparator.d.mts +22 -19
  71. package/dist/v1/libs/TinyArrayComparator.mjs +14 -11
  72. package/dist/v1/libs/TinyArrayPaginator.cjs +23 -16
  73. package/dist/v1/libs/TinyArrayPaginator.d.mts +57 -30
  74. package/dist/v1/libs/TinyArrayPaginator.mjs +22 -16
  75. package/dist/v1/libs/TinyClassManager/TinyPluginInliner.cjs +265 -0
  76. package/dist/v1/libs/TinyClassManager/TinyPluginInliner.d.mts +92 -0
  77. package/dist/v1/libs/TinyClassManager/TinyPluginInliner.mjs +223 -0
  78. package/dist/v1/libs/TinyClassManager.cjs +52 -22
  79. package/dist/v1/libs/TinyClassManager.d.mts +26 -16
  80. package/dist/v1/libs/TinyClassManager.mjs +50 -21
  81. package/dist/v1/libs/TinyColorValidator.d.mts +16 -16
  82. package/dist/v1/libs/TinyDragDropDetector.cjs +9 -34
  83. package/dist/v1/libs/TinyDragDropDetector.d.mts +7 -21
  84. package/dist/v1/libs/TinyDragDropDetector.mjs +9 -24
  85. package/dist/v1/libs/TinyDragger.cjs +5 -5
  86. package/dist/v1/libs/TinyDragger.d.mts +4 -3
  87. package/dist/v1/libs/TinyDragger.mjs +5 -5
  88. package/dist/v1/libs/TinyElementObserver.cjs +6 -21
  89. package/dist/v1/libs/TinyElementObserver.d.mts +6 -15
  90. package/dist/v1/libs/TinyElementObserver.mjs +6 -20
  91. package/dist/v1/libs/TinyGamepad.cjs +4 -6
  92. package/dist/v1/libs/TinyGamepad.d.mts +1 -1
  93. package/dist/v1/libs/TinyHtml/TinyHtmlIcon.d.mts +1 -1
  94. package/dist/v1/libs/TinyHtml/TinyHtmlTemplate.d.mts +1 -1
  95. package/dist/v1/libs/TinyHtml/TinyHtmlTextarea.d.mts +1 -1
  96. package/dist/v1/libs/TinyHtml/index.d.mts +1 -1
  97. package/dist/v1/libs/TinyHtml.cjs +72 -98
  98. package/dist/v1/libs/TinyHtml.d.mts +15 -32
  99. package/dist/v1/libs/TinyHtml.mjs +53 -77
  100. package/dist/v1/libs/TinyInventory.d.mts +1 -1
  101. package/dist/v1/libs/TinyMediaPlayer.cjs +844 -0
  102. package/dist/v1/libs/TinyMediaPlayer.d.mts +306 -0
  103. package/dist/v1/libs/TinyMediaPlayer.mjs +755 -0
  104. package/dist/v1/libs/TinyRadioFm.cjs +1378 -0
  105. package/dist/v1/libs/TinyRadioFm.d.mts +554 -0
  106. package/dist/v1/libs/TinyRadioFm.mjs +1137 -0
  107. package/dist/v1/libs/TinySmartScroller.cjs +39 -26
  108. package/dist/v1/libs/TinySmartScroller.d.mts +16 -62
  109. package/dist/v1/libs/TinySmartScroller.mjs +35 -26
  110. package/dist/v1/libs/UltraRandomMsgGen.d.mts +7 -7
  111. package/docs/v1/README.md +6 -0
  112. package/docs/v1/basics/mediaContent.md +131 -0
  113. package/docs/v1/libs/TinyClassManager/TinyPluginInliner.md +107 -0
  114. package/docs/v1/libs/TinyDragDropDetector.md +5 -9
  115. package/docs/v1/libs/TinyElementObserver.md +3 -6
  116. package/docs/v1/libs/TinyHtml.md +0 -29
  117. package/docs/v1/libs/TinyMediaPlayer.md +100 -0
  118. package/docs/v1/libs/TinyRadioFm.md +141 -0
  119. package/docs/v1/libs/TinySmartScroller.md +3 -3
  120. package/package.json +40 -22
@@ -0,0 +1,131 @@
1
+ # ๐ŸŽต Media Content Module
2
+
3
+ Welcome to the documentation for the media content module! This module is the heart of the media processing pipeline, responsible for downloading audio, extracting rich metadata (ID3 tags), managing memory for image blobs, and preparing finalized `MediaContent` objects for the radio system. ๐Ÿš€
4
+
5
+ ---
6
+
7
+ ## ๐Ÿ“ฆ Data Structures & Types
8
+
9
+ These definitions ensure type safety and provide a clear blueprint for how media information is structured throughout the application.
10
+
11
+ ### ๐ŸŽผ Core Metadata Types
12
+
13
+ | Type | Description |
14
+ | :--- | :--- |
15
+ | `MediaContent` | **The Final Object.** A combination of `MediaContentBase` and `MediaContentMetadata`. This is what the system uses for playback. |
16
+ | `MediaContentBase` | The essential properties: `id`, `title`, `artist`, `duration`, `url`, and `weight`. |
17
+ | `MediaContentMetadata` | Rich metadata modeled after `music-metadata`. Includes `album`, `genre`, `year`, `track` info, etc. |
18
+ | `MediaNumber` | A structure for indexing: `{ no: number \| null, of: number \| null }`. |
19
+ | `UnknownArtistGetter` | A type that can be a `string` or a function returning a `string`. |
20
+
21
+ ### ๐Ÿ–ผ๏ธ Image & Picture Types
22
+
23
+ | Type | Description |
24
+ | :--- | :--- |
25
+ | `IPicture` | Represents an image attachment (like album art). |
26
+ | `IPictureTemplate<T>` | A generic template for pictures, where `T` is the data type (`Uint8Array` or `string`). |
27
+
28
+ ### ๐Ÿšฆ Progress & Error Types
29
+
30
+ | Type | Description |
31
+ | :--- | :--- |
32
+ | `LoadingMediaProgress` | Tracks the current state: `status` ('loading' or 'success'), `stage`, and the `url`. |
33
+ | `LoadingProgressStage` | Stages: `INITIALIZING` โžก๏ธ `DOWNLOADING` โžก๏ธ `METADATA_LOADED` โžก๏ธ `EXTRACTING_ID3` โžก๏ธ `COMPLETE`. |
34
+ | `LoadingErrorStage` | Error stages: `INITIALIZING`, `DOWNLOADING`, `METADATA`, `ID3`, or `UNKNOWN`. |
35
+
36
+ ---
37
+
38
+ ## โŒ Custom Errors
39
+
40
+ ### `MediaLoadingError`
41
+ Used to provide specific context when something goes wrong during the media preparation lifecycle.
42
+
43
+ * **Properties:**
44
+ * `message`: Human-readable error description.
45
+ * `url`: The URL that caused the failure.
46
+ * `stage`: The `LoadingErrorStage` where the error occurred.
47
+
48
+ ---
49
+
50
+ ## ๐Ÿ› ๏ธ Utility Functions
51
+
52
+ ### Factory & Validation
53
+
54
+ | Function | Description |
55
+ | :--- | :--- |
56
+ | `getMediaContentBase` | Returns a fresh, empty `MediaContentBase` object. |
57
+ | `getMediaContentMetadata` | Returns a fresh, empty `MediaContentMetadata` object. |
58
+ | `valMediaContentMetadata` | Performs strict validation on a full `MediaContentMetadata` object. |
59
+ | `valMediaContentMetadataPartial` | Performs validation on a partial metadata object (useful for patches/updates). |
60
+
61
+ ### Data & Memory Management
62
+
63
+ | Function | Description |
64
+ | :--- | :--- |
65
+ | `generateSimpleHash` | Generates a deterministic 8-character ID from a string using SHA-1. |
66
+ | `convertToBlobUrl` | Converts `Uint8Array` or Base64 strings into high-performance Blob URLs. It also tracks usage to prevent memory leaks. |
67
+ | `blobUrlToBase64` | Converts a `blob:` URL back into a Base64 Data URL. Useful for exporting data. |
68
+ | `revokeContentUrls` | Safely cleans up memory by revoking Blob URLs associated with a `MediaContent` object. **Call this when media is no longer needed!** ๐Ÿงน |
69
+
70
+ ---
71
+
72
+ ## ๐Ÿš€ Main Operations
73
+
74
+ ### `extractMediaId3Tags`
75
+ Downloads an audio file and uses a provided parser to extract its internal metadata.
76
+
77
+ * **Parameters:**
78
+ * `url` (string): The full URL of the audio file.
79
+ * `parseFile` (Function): A callback that accepts a `Blob` and returns a promise with metadata.
80
+ * **Returns:** `Promise<MediaContentMetadata>`
81
+ * **Throws:** `TypeError` for invalid arguments or `Error` for network/parsing failures.
82
+
83
+ ---
84
+
85
+ ### `parseMediaMetadata`
86
+ **The Primary Factory Method.** This is the main entry point for turning a URL or an Audio element into a fully prepared `MediaContent` object. It handles the heavy lifting: downloading, metadata extraction, fallback logic, and progress reporting.
87
+
88
+ #### ๐Ÿ“ Parameters
89
+
90
+ | Parameter | Type | Description |
91
+ | :--- | :--- | :--- |
92
+ | `source` | `string \| HTMLMediaElement` | The audio source (URL or Audio object). |
93
+ | `defaultMetadata` | `Partial<MediaContent>` | Default data for the metadata. |
94
+ | `metadata` | `Partial<MediaContent>` | Manual overrides for the metadata (highest priority). |
95
+ | `parseFile` | `ParseMediaContentMetadata` | The library function used to parse the file. |
96
+ | `callbacks` | `Object` | Contains `onProgress` and `onError` listeners. |
97
+ | `unknownArtist` | `UnknownArtistGetter` | Logic to determine the artist if none is found. |
98
+
99
+ #### ๐Ÿ’ก Usage Examples
100
+
101
+ **1. Using a simple URL with manual overrides:**
102
+ ```javascript
103
+ import { parseMediaMetadata } from './mediaContent.mjs';
104
+ import { parseBlob } from 'music-metadata';
105
+
106
+ // Note: We pass empty objects for default/manual if we want to jump straight to the parser
107
+ const track = await parseMediaMetadata(
108
+ '/assets/song.mp3',
109
+ {},
110
+ { title: 'Manual Override' },
111
+ parseBlob
112
+ );
113
+
114
+ console.log(track.title); // "Manual Override"
115
+ ```
116
+
117
+ **2. Using an existing Audio object with progress tracking:**
118
+ ```javascript
119
+ const audio = new Audio('/assets/song.mp3');
120
+
121
+ const track = await parseMediaMetadata(
122
+ audio,
123
+ {},
124
+ {},
125
+ parseBlob,
126
+ {
127
+ onProgress: (p) => console.log(`[${p.stage}] ${p.status}`),
128
+ onError: (e) => console.error(`Error at ${e.stage}: ${e.error.message}`)
129
+ }
130
+ );
131
+ ```
@@ -0,0 +1,107 @@
1
+ # ๐Ÿ› ๏ธ TinyPluginInliner
2
+
3
+ `TinyPluginInliner` is a powerful **build-time utility** designed specifically to complement the `TinyClassManager`.
4
+
5
+ When using `TinyClassManager`, you compose classes using a mixin pattern via the `.insert()` method. While this is incredibly flexible for development, it creates a challenge for deployment: you end up with many small, interconnected files and complex import paths.
6
+
7
+ **`TinyPluginInliner` solves this by "inlining" your plugins directly into a single, self-contained bundle.** It automates the process of fetching plugin code, rewriting their internal import paths, and hoisting dependencies to the top of your final file. ๐Ÿš€
8
+
9
+ ---
10
+
11
+ ## โœจ Key Features
12
+
13
+ * **๐Ÿ“ฆ Automatic Inlining:** It scans your entry file for `.insert(PluginName)` calls and replaces them with the actual source code of the plugin.
14
+ * **๐Ÿ”— Smart Path Rewriting:** It automatically corrects `import` statements inside your plugins so they point to the correct relative locations in your `dist` folder.
15
+ * **๐Ÿ“š JSDoc Support:** It doesn't just fix code; it also rewrites `import()` calls and `@typedef` paths inside JSDoc comments, ensuring your IDE (like VS Code) keeps providing perfect autocomplete in the bundled version.
16
+ * **๐ŸŒ NPM Integration:** It can reach into your `node_modules`, grab an NPM package used by a plugin, and inline it into your bundle, even fixing that package's internal relative paths!
17
+ * **๐Ÿ”„ Root Remapping:** Through `rootReplacement`, you can tell the inliner to automatically swap directory names (e.g., changing all `src/` imports to `dist/` imports).
18
+ * **๐Ÿ” Dependency Hoisting:** It collects all necessary imports and type definitions from all plugins and moves them to the very top of the generated file for a clean, valid structure.
19
+
20
+ ---
21
+
22
+ ## โš™๏ธ Configuration
23
+
24
+ When creating an instance of `TinyPluginInliner`, you provide an `InlinerConfig` object:
25
+
26
+ | Property | Type | Description |
27
+ | :--- | :--- | :--- |
28
+ | `entryPoint` | `string` | The absolute or relative path to your main file (where you call `.insert()`). |
29
+ | `outDir` | `string` | The directory where the final bundle will be saved. |
30
+ | `outFileName` | `string` | The name of the generated bundle file (e.g., `bundle.mjs`). |
31
+ | `rootReplacement` | `Object` | *(Optional)* An object `{ from: string, to: string }` to remap project roots. |
32
+
33
+ ---
34
+
35
+ ## ๐Ÿš€ How to Use
36
+
37
+ ### 1. Develop your Plugins
38
+ Your plugins must follow the `TinyClassManager` requirement of having static properties for identification.
39
+
40
+ ```javascript
41
+ // src/plugins/LoggerPlugin.mjs
42
+ export class LoggerPlugin {
43
+ static _tinyDepName = 'Logger';
44
+ static _tinyDeps = [];
45
+
46
+ constructor(Base) {
47
+ super();
48
+ this.log = (msg) => console.log(`[LOG]: ${msg}`);
49
+ }
50
+ }
51
+ ```
52
+
53
+ ### 2. Create your Entry Point
54
+ This is the file that uses the `TinyClassManager` to compose your final class.
55
+
56
+ ```javascript
57
+ // src/main.mjs
58
+ import TinyClassManager from './libs/TinyClassManager.mjs';
59
+ import { LoggerPlugin } from './plugins/LoggerPlugin.mjs';
60
+
61
+ class BaseUser {}
62
+
63
+ // The Inliner will look for this specific pattern!
64
+ const User = new TinyClassManager(BaseUser)
65
+ .insert(LoggerPlugin)
66
+ .build();
67
+
68
+ export default User;
69
+ ```
70
+
71
+ ### 3. Run the Inliner
72
+ Create a build script (e.g., `build.mjs`) to trigger the process.
73
+
74
+ ```javascript
75
+ import TinyPluginInliner from './libs/TinyClassManager/TinyPluginInliner.mjs';
76
+
77
+ const builder = new TinyPluginInliner({
78
+ entryPoint: 'src/main.mjs',
79
+ outDir: 'dist',
80
+ outFileName: 'app.bundle.mjs',
81
+ // This ensures that if a plugin imports from 'src/utils',
82
+ // it will be rewritten to 'dist/utils' in the bundle.
83
+ rootReplacement: {
84
+ from: 'src',
85
+ to: 'dist',
86
+ },
87
+ });
88
+
89
+ builder.build();
90
+ ```
91
+
92
+ ---
93
+
94
+ ## ๐Ÿ”„ Before vs. After
95
+
96
+ ### ๐Ÿ“ Before (Development)
97
+ Your project is a web of files:
98
+ * `src/main.mjs` $\rightarrow$ imports `LoggerPlugin`
99
+ * `src/plugins/LoggerPlugin.mjs` $\rightarrow$ imports `src/utils/helper.mjs`
100
+
101
+ ### ๐Ÿ“ฆ After (Production Bundle)
102
+ The `dist/app.bundle.mjs` is a single, massive, but perfectly valid file:
103
+ 1. **Top:** All imports from `main.mjs`, `LoggerPlugin.mjs`, and `helper.mjs` are hoisted here.
104
+ 2. **Middle:** All `@typedef` definitions are gathered here.
105
+ 3. **Bottom:** The logic of `main.mjs` runs, but the `.insert(LoggerPlugin)` call has been replaced by the actual code of the `LoggerPlugin` class, which now correctly references the hoisted helper functions.
106
+
107
+ **Everything is ready to be shipped!** ๐Ÿšข๐Ÿ’จ
@@ -21,7 +21,6 @@ A lightweight JavaScript utility for detecting drag-and-drop file operations on
21
21
  import TinyDragDropDetector from './TinyDragDropDetector.js';
22
22
 
23
23
  const dnd = new TinyDragDropDetector({
24
- fullscreen: false,
25
24
  target: document.getElementById('drop-area'),
26
25
  hoverClass: 'hover-effect',
27
26
  onDrop: (files, event) => {
@@ -50,19 +49,19 @@ A drag-and-drop detector instance.
50
49
  ### Constructor
51
50
 
52
51
  ```javascript
53
- new TinyDragDropDetector(options)
52
+ new TinyDragDropDetector(target, options)
54
53
  ```
55
54
 
56
55
  #### Parameters:
57
56
 
58
- | Name | Type | Default | Description |
59
- | --------- | ------------------------------------------- | ------- | --------------------- |
60
- | `options` | [`DragAndDropOptions`](#draganddropoptions) | `{}` | Configuration object. |
57
+ | Name | Type | Description |
58
+ | --------- | ------------------------------------------- | ------------------------------------------------------- |
59
+ | `target` | `HTMLElement` | The DOM element where drag listeners will be attached. |
60
+ | `options` | [`DragAndDropOptions`](#draganddropoptions) | Configuration object. |
61
61
 
62
62
  #### Throws:
63
63
 
64
64
  * `TypeError` if `target` is not an `HTMLElement`.
65
- * `TypeError` if `fullscreen` is not a boolean.
66
65
  * `TypeError` if `hoverClass` is not a string.
67
66
  * `TypeError` if `onDrop` is not a function.
68
67
  * `TypeError` if `onEnter` is defined but not a function.
@@ -76,7 +75,6 @@ new TinyDragDropDetector(options)
76
75
  | ----------------- | ------------- | ------------------------------------------------------------------------ |
77
76
  | `getTarget()` | `HTMLElement` | Returns the DOM element where listeners are attached. |
78
77
  | `getHoverClass()` | `string` | Returns the CSS class applied during drag hover. |
79
- | `isFullScreen()` | `boolean` | Indicates whether the detector is in fullscreen mode (`document.body`). |
80
78
  | `isDragging()` | `boolean` | Returns whether a drag operation is currently active over the target. |
81
79
  | `bound()` | `boolean` | Returns whether the event listeners are currently bound to the target. |
82
80
  | `destroy()` | `void` | Destroys the detector, unbinding all events and cleaning up CSS classes. |
@@ -98,8 +96,6 @@ new TinyDragDropDetector(options)
98
96
  ```javascript
99
97
  /**
100
98
  * @typedef {Object} DragAndDropOptions
101
- * @property {HTMLElement} [target=document.body] - The DOM element where drag listeners will be attached. Defaults to `document.body` if `fullscreen` is true or no target is provided.
102
- * @property {boolean} [fullscreen=true] - If true, listeners are attached to the entire page (`document.body`). If false, the `target` must be specified.
103
99
  * @property {string} [hoverClass="dnd-hover"] - CSS class applied to the target element while files are being dragged over it.
104
100
  * @property {(files: FileList, event: DragEvent) => void} onDrop - Callback function executed when files are dropped onto the target.
105
101
  * @property {(event: DragEvent) => void} [onEnter] - Optional callback triggered when dragging enters the target area.
@@ -22,14 +22,13 @@ type ElementDetectorsFn = (
22
22
  ## ๐Ÿ”น Constructor
23
23
 
24
24
  ```ts
25
- new TinyElementObserver({
26
- el?: Element,
25
+ new TinyElementObserver(el, {
27
26
  initDetectors?: Array<[string, ElementDetectorsFn]>,
28
27
  initCfg?: MutationObserverInit
29
28
  })
30
29
  ```
31
30
 
32
- * **`el`** *(optional)* โ†’ Initial element to observe ๐Ÿท๏ธ
31
+ * **`el`** โ†’ Initial element to observe ๐Ÿท๏ธ
33
32
  * **`initDetectors`** *(optional)* โ†’ Array of detectors to register on creation ๐ŸŽ›๏ธ
34
33
  * **`initCfg`** *(optional)* โ†’ Initial `MutationObserverInit` configuration โš™๏ธ
35
34
 
@@ -39,7 +38,6 @@ new TinyElementObserver({
39
38
 
40
39
  | Property | Type | Description |
41
40
  | ----------- | ------------------------------------- | --------------------------------------------------------- |
42
- | `el` | `Element \| undefined` | The DOM element being observed. Can only be set once. ๐Ÿ—๏ธ |
43
41
  | `settings` | `MutationObserverInit` | Observer configuration object. ๐Ÿ”ง |
44
42
  | `observer` | `MutationObserver \| null` | Internal observer instance. |
45
43
  | `detectors` | `Array<[string, ElementDetectorsFn]>` | List of registered detectors. |
@@ -85,8 +83,7 @@ const logDetector = (mutation, index, mutations) => {
85
83
  };
86
84
 
87
85
  // Create instance
88
- const observer = new TinyElementObserver({
89
- el: document.querySelector('#target'),
86
+ const observer = new TinyElementObserver(document.querySelector('#target'), {
90
87
  initDetectors: [['logger', logDetector]],
91
88
  initCfg: { childList: true, subtree: true }
92
89
  });
@@ -767,32 +767,6 @@ Finds elements by tag name in a namespace (defaults to XHTML).
767
767
 
768
768
  ## ๐Ÿ” Element Observer
769
769
 
770
- ### โšก `autoStartElemObserver`
771
-
772
- A **flag** that determines whether the internal element observer should automatically start when some library code initializes.
773
-
774
- * **Type** ๐Ÿ”˜ โ€” `boolean`
775
- * **Default** ๐Ÿ โ€” `true`
776
-
777
- #### ๐Ÿ“ฅ Getter
778
-
779
- ```js
780
- TinyHtml.autoStartElemObserver; // returns true or false
781
- ```
782
-
783
- Returns the current state of the auto-start flag.
784
-
785
- #### ๐Ÿ“ค Setter
786
-
787
- ```js
788
- TinyHtml.autoStartElemObserver = false;
789
- ```
790
-
791
- Updates the flag.
792
- โš ๏ธ Throws a `TypeError` if the value is not a boolean.
793
-
794
- ---
795
-
796
770
  ### ๐Ÿ‘€ `tinyObserver`
797
771
 
798
772
  The internal **[`TinyElementObserver`](./TinyElementObserver.md)** instance used by TinyHtml to track DOM changes.
@@ -857,9 +831,6 @@ document.addEventListener('tinyhtml.classchanged', (e) => {
857
831
  console.log('๐Ÿท๏ธ Class changed:', e.detail);
858
832
  });
859
833
 
860
- // Disable auto-start (if needed)
861
- TinyHtml.autoStartElemObserver = false;
862
-
863
834
  // Manually start the observer
864
835
  TinyHtml.tinyObserver.start();
865
836
  ```
@@ -0,0 +1,100 @@
1
+ # ๐ŸŽง TinyMediaPlayer Documentation
2
+
3
+ `TinyMediaPlayer` is a robust, universal media player manager designed to orchestrate playback across multiple different media providers (such as YouTube, Spotify, etc.) using an **Adapter Pattern**.
4
+
5
+ ---
6
+
7
+ ## ๐Ÿงฉ Core Architecture: The Adapter Pattern
8
+
9
+ To support different media platforms, `TinyMediaPlayer` relies on **Adapters**. You must create a class that extends `BaseMediaAdapter` for every new service you wish to support.
10
+
11
+ ### ๐Ÿ› ๏ธ `BaseMediaAdapter` (Abstract Class)
12
+ *This class cannot be instantiated directly. It serves as an interface.*
13
+
14
+ | Method | Description | Parameters | Returns |
15
+ | :--- | :--- | :--- | :--- |
16
+ | `canHandle` | Checks if the adapter supports a specific content item. | `content: MediaContent` | `boolean` |
17
+ | `play` | Starts or resumes playback. | `content: MediaContent` | `Promise<void>` |
18
+ | `pause` | Pauses the current playback. | None | `Promise<void>` |
19
+ | `stop` | Stops playback and resets the platform state. | None | `Promise<void>` |
20
+ | `seek` | Jumps to a specific time. | `timeMs: number` | `Promise<void>` |
21
+ | `getCurrentTime` | Gets the current playback position. | None | `number` (ms) |
22
+ | `setVolume` | Sets the volume level. | `volume: number` (0.0 to 1.0) | `void` |
23
+
24
+ ---
25
+
26
+ ## ๐Ÿš€ `TinyMediaPlayer` Class
27
+
28
+ The main controller for managing playlists, playback state, and adapters.
29
+
30
+ ### โš™๏ธ Configuration (Constructor Options)
31
+ When initializing `new TinyMediaPlayer(options)`, you can pass the following:
32
+
33
+ * **`persistVolume`** (`boolean`): If `true`, the volume level is automatically saved to and loaded from `localStorage`. (Default: `false`).
34
+ * **`volumeStorageKey`** (`string`): The key name used in `localStorage` to store the volume. (Default: `'tiny_media_player_volume'`).
35
+
36
+ ---
37
+
38
+ ### ๐Ÿ—๏ธ Static Members
39
+
40
+ #### Properties
41
+ * **`unknownArtist`**: A global setting for when an artist's name is unavailable.
42
+ * **Type**: `string` | `function` (returns a string).
43
+ * **Getter/Setter**: Allows you to define a custom string or a logic-based function.
44
+
45
+ #### Methods
46
+ * **`parseContent(source, defaultMetadata, metadata, parseFile, callbacks)`**:
47
+ * A static factory method to prepare a `MediaContent` object by extracting metadata from a URL or HTMLMediaElement.
48
+ * **Returns**: `Promise<MediaContent>`.
49
+
50
+ ---
51
+
52
+ ### ๐Ÿ“Š Instance Properties (Getters & Setters)
53
+
54
+ | Property | Type | Description |
55
+ | :--- | :--- | :--- |
56
+ | `playlist` | `MediaContent[]` | Returns a shallow copy of the current playlist. |
57
+ | `currentIndex` | `number` | The index of the currently playing track. |
58
+ | `loopMode` | `'NONE' \| 'TRACK' \| 'PLAYLIST'` | Defines how the player behaves at the end of a track. |
59
+ | `isRandom` | `boolean` | Enables or disables shuffle mode. |
60
+ | `isPlaying` | `boolean` | Indicates if media is currently playing. |
61
+ | `volume` | `number` | The current volume (constrained between `0.0` and `1.0`). |
62
+ | `persistVolume` | `boolean` | Enables/disables volume saving to `localStorage`. |
63
+ | `volumeStorageKey`| `string` | The key used for volume persistence. |
64
+
65
+ ---
66
+
67
+ ### ๐Ÿ•น๏ธ Methods
68
+
69
+ #### ๐Ÿ”Œ Adapter Management
70
+ * **`registerAdapter(id, adapter)`**: Registers a new provider.
71
+ * `id`: A unique string (e.g., `'youtube'`).
72
+ * `adapter`: An instance of a class extending `BaseMediaAdapter`.
73
+
74
+ #### ๐Ÿ“‹ Playlist Management
75
+ * **`addTrack(content)`**: Adds a new `MediaContent` object to the end of the playlist.
76
+ * **`existsTrack(index)`**: Returns `true` if a track exists at the given index.
77
+ * **`getTrack(index)`**: Retrieves the track at the specified index.
78
+ * **`removeTrack(index)`**: Removes a track. If the current track is removed, playback stops or adjusts.
79
+ * **`searchTrack(query)`**: Searches the playlist.
80
+ * `query`: A `string` (searches title, artist, or album) or a `function` (custom logic).
81
+ * **Returns**: `SearchResult[]` (array of objects containing the track and its index).
82
+ * **`clearPlaylist()`**: Stops playback and empties the playlist.
83
+
84
+ #### โฏ๏ธ Playback Controls
85
+ * **`play()`**: Starts playback of the current track.
86
+ * **`pause()`**: Pauses the current track.
87
+ * **`stop()`**: Stops the current track completely.
88
+ * **`next()`**: Advances to the next track (respects `loopMode` and `isRandom`).
89
+ * **`prev()`**: Returns to the previous track (respects `loopMode` and `isRandom`).
90
+ * **`seek(timeMs)`**: Jumps to a specific millisecond in the current track.
91
+ * **`step(stepMs)`**: Moves the timeline forward (positive) or backward (negative) by a specific amount.
92
+
93
+ ---
94
+
95
+ ## โš ๏ธ Error Handling
96
+
97
+ The class uses strict validation. The following errors may be thrown:
98
+ * **`TypeError`**: Thrown when an argument is of the wrong type (e.g., passing a string to `volume`).
99
+ * **`RangeError`**: Thrown when a number is out of allowed bounds (e.g., an index that doesn't exist or volume > 1.0).
100
+ * **`Error`**: Thrown if no compatible adapter is found for the current content.
@@ -0,0 +1,141 @@
1
+ # ๐Ÿ“ป TinyRadioFm Documentation
2
+
3
+ `TinyRadioFm` is a powerful, deterministic, and seed-based radio management system. It is designed to handle complex playback sequences, including music and voice playlists, scheduled timeline mutations, and custom content injections, all while maintaining absolute reproducibility through a mathematical seed. ๐ŸŽฏ
4
+
5
+ > **Beta Status:** This engine is currently in beta.
6
+
7
+ ---
8
+
9
+ ## โœจ Key Features
10
+
11
+ * **๐ŸŽฏ Deterministic Playback:** Uses a `Mulberry32` PRNG to ensure that the same seed always produces the exact same radio sequence.
12
+ * **๐ŸŽต Dual-Stream Logic:** Manages separate playlists for `music` and `voice`, with configurable interaction rules (e.g., playing voices after music).
13
+ * **๐Ÿ“… Scheduled Mutations:** Schedule tasks to `add`, `remove`, or `move` items in the playlist at specific future timestamps.
14
+ * **โšก Custom Injections:** Inject "Custom Positions" into the timeline that interrupt the standard cycle.
15
+ * **๐Ÿง  Smart Queuing:** Intelligent logic to prevent audio overlaps by automatically adjusting timestamps to the end of currently playing content.
16
+ * **๐Ÿ’พ State Persistence:** Full export/import capabilities, including automatic conversion of Blob URLs to Base64 for easy storage.
17
+ * **๐Ÿงน Memory Management:** Proactive cleanup of Blob URLs and event listeners to prevent memory leaks.
18
+
19
+ ---
20
+
21
+ ## โš™๏ธ Configuration
22
+
23
+ The behavior of the engine is controlled via the `RadioConfig` object.
24
+
25
+ | Property | Type | Default | Description |
26
+ | :--- | :--- | :--- | :--- |
27
+ | `mode` | `'playlist' \| 'random'` | `'playlist'` | Sequence logic for the music playlist. |
28
+ | `voiceMode` | `'playlist' \| 'random'` | `'playlist'` | Sequence logic for the voice playlist. |
29
+ | `silenceDuration` | `number` | `0` | Gap in milliseconds between tracks. |
30
+ | `queryLimit` | `number` | `100000` | Safety lock for maximum items processed during queries. |
31
+ | `voiceAfterMusic` | `boolean` | `true` | If `true`, voice messages play after music tracks. |
32
+ | `voiceMin` | `number` | `0` | Minimum number of voice messages to play in a cycle. |
33
+ | `voiceMax` | `number` | `1` | Maximum number of voice messages to play in a cycle. |
34
+ | `musicMaxConsecutive`| `number` | `0` | Max consecutive music repeats (`-1` = unlimited, `0` = no repeats). |
35
+ | `voiceMaxConsecutive`| `number` | `0` | Max consecutive voice repeats (`-1` = unlimited, `0` = no repeats). |
36
+
37
+ ---
38
+
39
+ ## ๐Ÿ› ๏ธ Core API Reference
40
+
41
+ ### ๐Ÿ—๏ธ Static Methods
42
+
43
+ #### `static parseContent(source, defaultMetadata, metadata, parseFile, callbacks)`
44
+ Prepares a `MediaContent` object by extracting metadata from an audio source.
45
+ * **Parameters:**
46
+ * `source`: A URL string or an existing `HTMLMediaElement`.
47
+ * `defaultMetadata`: Optional base metadata.
48
+ * `metadata`: Optional manual overrides.
49
+ * `parseFile`: Internal helper for file parsing.
50
+ * `callbacks`: Object containing `onProgress` and `onError` handlers.
51
+ * **Returns:** `Promise<MediaContent>`
52
+ * **Throws:** `MediaLoadingError`
53
+
54
+ ---
55
+
56
+ ### ๐Ÿ“ฅ Content Management
57
+
58
+ #### `add(type, data, smartQueue = true)`
59
+ Adds new content to the engine immediately.
60
+ * **Parameters:**
61
+ * `type`: `'music' | 'voice' | 'custom'`.
62
+ * `data`: The `MediaContent` object (must include `id` and `duration`).
63
+ * `smartQueue`: If `true`, delays insertion until the current content ends (primarily for `custom`).
64
+ * **Emits:** `contentAdded`
65
+
66
+ #### `remove(id)`
67
+ Instantly removes content by its unique ID across all lists, custom positions, and pending tasks. It also revokes associated Blob URLs.
68
+ * **Parameters:** `id` (string)
69
+ * **Emits:** `contentRemoved`
70
+
71
+ ---
72
+
73
+ ### ๐Ÿ“… Task & Config Management
74
+
75
+ #### `scheduleTask(timestamp, action, type, payload, smartQueue = true)`
76
+ Schedules a modification to the playlists at a specific point in time.
77
+ * **Parameters:**
78
+ * `timestamp`: Target epoch timestamp (ms).
79
+ * `action`: `'add' | 'remove' | 'move'`.
80
+ * `type`: `'music' | 'voice'`.
81
+ * `payload`: Data required for the action (`MediaContent` for add, `string` for remove, or `ScheduledMovePayload` for move).
82
+ * `smartQueue`: If `true`, aligns the task to the end of the current event.
83
+ * **Emits:** `taskScheduled`
84
+
85
+ #### `setConfig(config)`
86
+ Performs a partial or full update of the engine configuration.
87
+ * **Parameters:** `config` (Partial or full `RadioConfig` object).
88
+ * **Emits:** `configChanged`
89
+
90
+ ---
91
+
92
+ ### ๐Ÿ” Timeline & Querying
93
+
94
+ #### `getCurrentEvent()`
95
+ Retrieves the exact event currently playing at the system's current time.
96
+ * **Returns:** `RadioEvent | null`
97
+
98
+ #### `queryTimeline(targetDate, limit = 10)`
99
+ Predicts upcoming events from a specific date forward using a virtual sandbox to prevent state mutation.
100
+ * **Parameters:**
101
+ * `targetDate`: Starting epoch timestamp.
102
+ * `limit`: Maximum number of events to return.
103
+ * **Returns:** `RadioEvent[]`
104
+ * **Throws:** `TypeError` or `RangeError` if limit is invalid.
105
+
106
+ ---
107
+
108
+ ### ๐Ÿ’พ Persistence & Lifecycle
109
+
110
+ #### `async exportState()`
111
+ Exports the entire radio state (playlists, tasks, seed, config) as a JSON string. Automatically converts all image Blob URLs to **Base64** for portability.
112
+ * **Returns:** `Promise<string>`
113
+
114
+ #### `importState(json)`
115
+ Hydrates the engine with a previously exported state.
116
+ * **Parameters:** `json` (A JSON string or a state object).
117
+ * **Emits:** `stateImported`
118
+
119
+ #### `destroy(destroyThumbs = true)`
120
+ Performs a complete cleanup. It wipes all lists, clears the cache, removes all listeners, and revokes all Blob URLs to free up system memory.
121
+ * **Parameters:** `destroyThumbs` (boolean) - Whether to revoke image Blob URLs.
122
+ * **Emits:** `destroyed`
123
+
124
+ ---
125
+
126
+ ## ๐Ÿ“Š Data Models (Summary)
127
+
128
+ ### `RadioEvent` ๐Ÿ“ก
129
+ The standardized object returned when querying the timeline.
130
+ * `id`, `title`, `artist`, `url`, `duration`
131
+ * `absoluteStart`, `absoluteEnd`
132
+ * `elapsedTime`, `remainingTime`
133
+ * `progress` (0 to 1)
134
+ * `isCustom` (boolean)
135
+
136
+ ### `ScheduledTask` ๐Ÿ“
137
+ An instruction to change the radio state.
138
+ * `timestamp`: Execution time.
139
+ * `action`: `'add' | 'remove' | 'move'`.
140
+ * `type`: `'music' | 'voice'`.
141
+ * `payload`: Action-specific data.
@@ -358,15 +358,15 @@ scroller._listenLoadEvents(imgElement);
358
358
 
359
359
  ## ๐Ÿงฑ DOM and Element Accessors
360
360
 
361
- ### ๐ŸŽฏ `target`
361
+ ### ๐ŸŽฏ `targetElement`
362
362
 
363
363
  Returns the internal scroll container element being monitored.
364
364
 
365
365
  ```js
366
- const container = scroller.target;
366
+ const container = scroller.targetElement;
367
367
  ```
368
368
 
369
- * ๐Ÿ”™ Returns the target DOM element (or `window` if in window mode).
369
+ * ๐Ÿ”™ Returns the target DOM element.
370
370
 
371
371
  ### ๐Ÿ•ต๏ธโ€โ™€๏ธ `getOldSize(el)`
372
372