@streamscloud/kit 0.0.1-1771006476137 → 0.0.1-1771075495185

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 (51) hide show
  1. package/dist/ui/player/buttons/cmp.mobile-player-buttons.svelte +37 -0
  2. package/dist/ui/player/buttons/cmp.mobile-player-buttons.svelte.d.ts +7 -0
  3. package/dist/ui/player/buttons/cmp.player-buttons.svelte +127 -0
  4. package/dist/ui/player/buttons/cmp.player-buttons.svelte.d.ts +9 -0
  5. package/dist/ui/player/buttons/cmp.responsive-player-buttons.svelte +32 -0
  6. package/dist/ui/player/buttons/cmp.responsive-player-buttons.svelte.d.ts +8 -0
  7. package/dist/ui/player/buttons/index.d.ts +4 -0
  8. package/dist/ui/player/buttons/index.js +3 -0
  9. package/dist/ui/player/buttons/types.d.ts +7 -0
  10. package/dist/ui/player/buttons/types.js +1 -0
  11. package/dist/ui/player/carousel/carousel-localization.d.ts +3 -0
  12. package/dist/ui/player/carousel/carousel-localization.js +12 -0
  13. package/dist/ui/player/carousel/cmp.carousel.svelte +418 -0
  14. package/dist/ui/player/carousel/cmp.carousel.svelte.d.ts +38 -0
  15. package/dist/ui/player/carousel/index.d.ts +2 -0
  16. package/dist/ui/player/carousel/index.js +1 -0
  17. package/dist/ui/player/carousel/types.d.ts +1 -0
  18. package/dist/ui/player/carousel/types.js +1 -0
  19. package/dist/ui/player/feed-slider/cmp.feed-slider.svelte +206 -0
  20. package/dist/ui/player/feed-slider/cmp.feed-slider.svelte.d.ts +43 -0
  21. package/dist/ui/player/feed-slider/index.d.ts +3 -0
  22. package/dist/ui/player/feed-slider/index.js +2 -0
  23. package/dist/ui/player/feed-slider/prevent-feed-scroll.d.ts +5 -0
  24. package/dist/ui/player/feed-slider/prevent-feed-scroll.js +11 -0
  25. package/dist/ui/player/feed-slider/types.d.ts +16 -0
  26. package/dist/ui/player/feed-slider/types.js +1 -0
  27. package/dist/ui/player/feed-slider/wheel-gestures-adapter.d.ts +17 -0
  28. package/dist/ui/player/feed-slider/wheel-gestures-adapter.js +79 -0
  29. package/dist/ui/player/providers/chunks-player-buffer/index.d.ts +2 -0
  30. package/dist/ui/player/providers/chunks-player-buffer/index.js +2 -0
  31. package/dist/ui/player/providers/chunks-player-buffer/player-chunk-item.svelte.d.ts +9 -0
  32. package/dist/ui/player/providers/chunks-player-buffer/player-chunk-item.svelte.js +9 -0
  33. package/dist/ui/player/providers/chunks-player-buffer/player-chunk.svelte.d.ts +30 -0
  34. package/dist/ui/player/providers/chunks-player-buffer/player-chunk.svelte.js +72 -0
  35. package/dist/ui/player/providers/chunks-player-buffer/player-chunks-manager.svelte.d.ts +23 -0
  36. package/dist/ui/player/providers/chunks-player-buffer/player-chunks-manager.svelte.js +220 -0
  37. package/dist/ui/player/providers/default-chunks-player-buffer.svelte.d.ts +23 -0
  38. package/dist/ui/player/providers/default-chunks-player-buffer.svelte.js +71 -0
  39. package/dist/ui/player/providers/default-feed-player-buffer.svelte.d.ts +29 -0
  40. package/dist/ui/player/providers/default-feed-player-buffer.svelte.js +121 -0
  41. package/dist/ui/player/providers/index.d.ts +4 -0
  42. package/dist/ui/player/providers/index.js +3 -0
  43. package/dist/ui/player/providers/service.d.ts +2 -0
  44. package/dist/ui/player/providers/service.js +13 -0
  45. package/dist/ui/player/providers/types.d.ts +54 -0
  46. package/dist/ui/player/providers/types.js +1 -0
  47. package/dist/ui/player/utils/index.d.ts +1 -0
  48. package/dist/ui/player/utils/index.js +1 -0
  49. package/dist/ui/player/utils/touch-synchronizer.d.ts +7 -0
  50. package/dist/ui/player/utils/touch-synchronizer.js +21 -0
  51. package/package.json +23 -5
@@ -0,0 +1,220 @@
1
+ import { Deferred } from '../../../../core';
2
+ import { PlayerChunk } from './player-chunk.svelte';
3
+ const ITEMS_BUFFER_SIZE = 10;
4
+ // Configuration: if true, always start from first item when switching chunks
5
+ // if false, activate the exact item at its position in the new chunk
6
+ const START_FROM_FIRST_ITEM_ON_CHUNK_SWITCH = true;
7
+ export class PlayerChunksManager {
8
+ provider;
9
+ _activeChunkIndex = $state(-1);
10
+ _loadedChunks = $state.raw([]);
11
+ _warmUpDeferred = $state.raw(null);
12
+ constructor(provider) {
13
+ this.provider = provider;
14
+ }
15
+ get activeChunk() {
16
+ return this._loadedChunks[this._activeChunkIndex] ?? null;
17
+ }
18
+ get loadedChunks() {
19
+ return this._loadedChunks;
20
+ }
21
+ get isLoading() {
22
+ return this._warmUpDeferred !== null || this._loadedChunks.some((c) => c.isLoading);
23
+ }
24
+ get flattenedChunkItems() {
25
+ return this._loadedChunks.reduce((acc, chunk) => {
26
+ acc.push(...chunk.items);
27
+ return acc;
28
+ }, []);
29
+ }
30
+ get flattenedActiveChunkItemIndex() {
31
+ if (!this.activeChunk || !this.activeChunk.activeChunkItem) {
32
+ return -1;
33
+ }
34
+ let itemsBeforeActiveChunk = 0;
35
+ for (let i = 0; i < this._activeChunkIndex; i++) {
36
+ itemsBeforeActiveChunk += this._loadedChunks[i].items.length;
37
+ }
38
+ return itemsBeforeActiveChunk + this.activeChunk.activeItemIndex;
39
+ }
40
+ tryActivateItemById = (id) => {
41
+ const chunkWithItemIndex = this._loadedChunks.findIndex((chunk) => chunk.items.some((item) => item.id === id));
42
+ const chunkWithItem = this._loadedChunks[chunkWithItemIndex];
43
+ if (!chunkWithItem) {
44
+ return false;
45
+ }
46
+ const itemIndex = chunkWithItem.items.findIndex((item) => item.id === id);
47
+ this.setActiveChunkIndex(chunkWithItemIndex, itemIndex);
48
+ return true;
49
+ };
50
+ removeItemById = (id) => {
51
+ const itemIndex = this.flattenedChunkItems.findIndex((item) => item.id === id);
52
+ if (itemIndex === -1) {
53
+ return false;
54
+ }
55
+ const currentItem = this.flattenedChunkItems[this.flattenedActiveChunkItemIndex];
56
+ // Find next item to activate BEFORE removing (if removing current item)
57
+ let nextItemId = currentItem?.id;
58
+ if (this.flattenedActiveChunkItemIndex === itemIndex) {
59
+ // Try next item
60
+ if (itemIndex + 1 < this.flattenedChunkItems.length) {
61
+ nextItemId = this.flattenedChunkItems[itemIndex + 1].id;
62
+ }
63
+ // Try previous item
64
+ else if (itemIndex > 0) {
65
+ nextItemId = this.flattenedChunkItems[itemIndex - 1].id;
66
+ }
67
+ else {
68
+ // No items left
69
+ nextItemId = null;
70
+ }
71
+ }
72
+ // Remove item from chunks
73
+ this._loadedChunks.forEach((chunk) => {
74
+ if (chunk.chunkItems.some((i) => i.model.id === id)) {
75
+ chunk.mutateChunkItems(chunk.chunkItems.filter((i) => i.model.id !== id));
76
+ }
77
+ });
78
+ if (nextItemId) {
79
+ this.tryActivateItemById(nextItemId);
80
+ }
81
+ else {
82
+ this._activeChunkIndex = -1;
83
+ }
84
+ return true;
85
+ };
86
+ removeChunkById = (id) => {
87
+ const chunkIndex = this._loadedChunks.findIndex((chunk) => chunk.model.id === id);
88
+ if (chunkIndex === -1) {
89
+ return false;
90
+ }
91
+ const isActiveChunk = this._activeChunkIndex === chunkIndex;
92
+ let newActiveChunkIndex = this._activeChunkIndex;
93
+ this._loadedChunks = [...this._loadedChunks.slice(0, chunkIndex), ...this._loadedChunks.slice(chunkIndex + 1)];
94
+ if (this._loadedChunks.length === 0) {
95
+ this._activeChunkIndex = -1;
96
+ this.provider.onEndReached?.();
97
+ return;
98
+ }
99
+ if (isActiveChunk) {
100
+ if (chunkIndex < this._loadedChunks.length) {
101
+ newActiveChunkIndex = chunkIndex;
102
+ }
103
+ else {
104
+ newActiveChunkIndex = this._loadedChunks.length - 1;
105
+ }
106
+ }
107
+ else if (this._activeChunkIndex > chunkIndex) {
108
+ newActiveChunkIndex--;
109
+ }
110
+ this.setActiveChunkIndex(newActiveChunkIndex, 0);
111
+ return true;
112
+ };
113
+ initialize = async () => {
114
+ this._loadedChunks = this.provider.initialData.prefetchedChunks.map((chunk) => new PlayerChunk({ chunk, provider: { loadChunkItems: this.provider.loadChunkItems } }));
115
+ await this.warmUp();
116
+ if (this._loadedChunks.length === 0 || this.flattenedChunkItems.length === 0) {
117
+ this.provider.onEndReached?.();
118
+ return;
119
+ }
120
+ const firstFilledChunkIndex = this._loadedChunks.findIndex((c) => c.items.length > 0);
121
+ const initialStartItemIndex = firstFilledChunkIndex === 0 && this.provider.initialData.startItemIndex && this.provider.initialData.startItemIndex > 0
122
+ ? this.provider.initialData.startItemIndex
123
+ : 0;
124
+ this.setActiveChunkIndex(firstFilledChunkIndex, initialStartItemIndex);
125
+ };
126
+ setActiveChunkIndex = async (index, chunkItemIndex) => {
127
+ this._activeChunkIndex = index;
128
+ this.activeChunk.setActiveItemIndex(chunkItemIndex);
129
+ // Don't wait for warm up to be finished, it runs in the background
130
+ this.warmUp();
131
+ };
132
+ activateItemAtFlattenedIndex = async (index) => {
133
+ const activeChunkId = this.activeChunk?.model.id;
134
+ const flattenedItems = this.loadedChunks.flatMap((x) => x.chunkItems);
135
+ const nextItem = flattenedItems[index];
136
+ if (!nextItem) {
137
+ return;
138
+ }
139
+ if (nextItem.chunkId !== activeChunkId) {
140
+ const nextChunkIndex = this.loadedChunks.findIndex((c) => c.model.id === nextItem.chunkId);
141
+ if (nextChunkIndex === -1) {
142
+ return;
143
+ }
144
+ const itemIndexInChunk = START_FROM_FIRST_ITEM_ON_CHUNK_SWITCH ? 0 : this.loadedChunks[nextChunkIndex].chunkItems.indexOf(nextItem);
145
+ this.setActiveChunkIndex(nextChunkIndex, itemIndexInChunk);
146
+ }
147
+ else {
148
+ this.activeChunk.setActiveItemIndex(this.activeChunk.chunkItems.indexOf(nextItem));
149
+ // Don't wait for warm up to be finished, it runs in the background
150
+ this.warmUp();
151
+ }
152
+ };
153
+ warmUp = async () => {
154
+ if (this._warmUpDeferred) {
155
+ return this._warmUpDeferred.promise;
156
+ }
157
+ this._warmUpDeferred = new Deferred();
158
+ try {
159
+ await this.warmUpSequentially();
160
+ }
161
+ finally {
162
+ this._warmUpDeferred.resolve();
163
+ this._warmUpDeferred = null;
164
+ }
165
+ };
166
+ reset = async () => {
167
+ this._activeChunkIndex = -1;
168
+ this._loadedChunks = [];
169
+ this._warmUpDeferred = null;
170
+ await this.warmUp();
171
+ };
172
+ warmUpSequentially = async () => {
173
+ const startChunkIndex = Math.max(0, this._activeChunkIndex);
174
+ // Calculate how many items we need ahead of current position
175
+ const getItemsAhead = () => {
176
+ const currentFlatIndex = this.flattenedActiveChunkItemIndex;
177
+ const totalItems = this.flattenedChunkItems.length;
178
+ return totalItems - currentFlatIndex - 1; // -1 because current item doesn't count
179
+ };
180
+ while (getItemsAhead() < ITEMS_BUFFER_SIZE) {
181
+ // Find first non-fully-loaded chunk starting from active
182
+ let targetChunkIndex = -1;
183
+ for (let i = startChunkIndex; i < this._loadedChunks.length; i++) {
184
+ if (this._loadedChunks[i].canLoadMore) {
185
+ targetChunkIndex = i;
186
+ break;
187
+ }
188
+ }
189
+ // If all chunks are fully loaded, load more chunks
190
+ if (targetChunkIndex === -1) {
191
+ const result = await this.provider.loadMoreChunks();
192
+ if (result.length === 0) {
193
+ // No more chunks available
194
+ break;
195
+ }
196
+ this._loadedChunks = [
197
+ ...this._loadedChunks,
198
+ ...result.map((chunk) => new PlayerChunk({
199
+ chunk,
200
+ provider: { loadChunkItems: this.provider.loadChunkItems }
201
+ }))
202
+ ];
203
+ continue; // Re-check for chunks to fill
204
+ }
205
+ // Load one page from target chunk
206
+ const chunk = this._loadedChunks[targetChunkIndex];
207
+ const itemsBefore = chunk.items.length;
208
+ await chunk.loadMore();
209
+ const itemsAfter = chunk.items.length;
210
+ // If chunk became fully loaded but added no items, continue to next chunk
211
+ if (itemsAfter === itemsBefore && !chunk.canLoadMore) {
212
+ continue;
213
+ }
214
+ // If no progress made and chunk is not fully loaded, something went wrong
215
+ if (itemsAfter === itemsBefore && chunk.canLoadMore) {
216
+ break; // Avoid infinite loop
217
+ }
218
+ }
219
+ };
220
+ }
@@ -0,0 +1,23 @@
1
+ import type { IChunksPlayerBuffer, IChunksPlayerDataProvider, WithId } from './types';
2
+ export declare class DefaultChunksPlayerBuffer<TItem extends WithId, TChunk extends WithId> implements IChunksPlayerBuffer<TItem> {
3
+ readonly kind = "chunks";
4
+ readonly loaded: TItem[];
5
+ readonly currentIndex: number;
6
+ readonly current: TItem | null;
7
+ readonly canLoadNext: boolean;
8
+ readonly canLoadPrevious: boolean;
9
+ readonly navigationDisabled: boolean;
10
+ readonly animationDuration = 500;
11
+ private _playerChunksManager;
12
+ private _onEndReachedFn;
13
+ constructor(provider: IChunksPlayerDataProvider<TItem, TChunk>);
14
+ get activeChunk(): import("./chunks-player-buffer").PlayerChunk<TItem, TChunk>;
15
+ setActiveChunkItemIndex: (index: number) => void;
16
+ tryActivateItemById: (id: string) => boolean;
17
+ removeChunkById: (id: string) => boolean | undefined;
18
+ removeItemById: (id: string) => boolean;
19
+ loadNext: () => Promise<void>;
20
+ loadPrevious: () => Promise<void>;
21
+ reset: () => void;
22
+ ensureWarmedUp: () => Promise<void>;
23
+ }
@@ -0,0 +1,71 @@
1
+ import { Utils } from '../../../core/utils';
2
+ import { PlayerChunksManager } from './chunks-player-buffer';
3
+ export class DefaultChunksPlayerBuffer {
4
+ kind = 'chunks';
5
+ loaded = $derived.by(() => this._playerChunksManager.flattenedChunkItems);
6
+ currentIndex = $derived.by(() => this._playerChunksManager.flattenedActiveChunkItemIndex);
7
+ current = $derived(this.currentIndex >= 0 ? this.loaded[this.currentIndex] : null);
8
+ canLoadNext = $derived.by(() => {
9
+ if (this.loaded.length && this.currentIndex < this.loaded.length - 1) {
10
+ return true;
11
+ }
12
+ // Check if manager is still loading
13
+ if (this._playerChunksManager.isLoading) {
14
+ return false; // Don't trigger onEndReached while loading
15
+ }
16
+ return !!this._onEndReachedFn;
17
+ });
18
+ canLoadPrevious = $derived(this.currentIndex > 0);
19
+ navigationDisabled = $derived(!this.canLoadNext && !this.canLoadPrevious);
20
+ animationDuration = 500;
21
+ _playerChunksManager;
22
+ _onEndReachedFn;
23
+ constructor(provider) {
24
+ this._onEndReachedFn = provider.onEndReached;
25
+ // Throttle navigation methods
26
+ this.loadNext = Utils.throttle(this.loadNext, this.animationDuration);
27
+ this.loadPrevious = Utils.throttle(this.loadPrevious, this.animationDuration);
28
+ this._playerChunksManager = new PlayerChunksManager(provider);
29
+ this._playerChunksManager.initialize();
30
+ }
31
+ get activeChunk() {
32
+ return this._playerChunksManager.activeChunk;
33
+ }
34
+ setActiveChunkItemIndex = (index) => {
35
+ if (!this.activeChunk) {
36
+ return;
37
+ }
38
+ this.activeChunk.setActiveItemIndex(index);
39
+ };
40
+ tryActivateItemById = (id) => {
41
+ return this._playerChunksManager.tryActivateItemById(id);
42
+ };
43
+ removeChunkById = (id) => {
44
+ return this._playerChunksManager.removeChunkById(id);
45
+ };
46
+ removeItemById = (id) => {
47
+ return this._playerChunksManager.removeItemById(id);
48
+ };
49
+ loadNext = async () => {
50
+ if (!this.canLoadNext) {
51
+ return;
52
+ }
53
+ if (this.currentIndex < this.loaded.length - 1) {
54
+ this._playerChunksManager.activateItemAtFlattenedIndex(this.currentIndex + 1);
55
+ return;
56
+ }
57
+ this._onEndReachedFn?.();
58
+ };
59
+ loadPrevious = async () => {
60
+ if (!this.canLoadPrevious) {
61
+ return;
62
+ }
63
+ this._playerChunksManager.activateItemAtFlattenedIndex(this.currentIndex - 1);
64
+ };
65
+ reset = () => {
66
+ this._playerChunksManager.reset();
67
+ };
68
+ ensureWarmedUp = async () => {
69
+ await this._playerChunksManager.warmUp();
70
+ };
71
+ }
@@ -0,0 +1,29 @@
1
+ import type { IFeedPlayerBuffer, IFeedPlayerDataProvider, WithId } from './types';
2
+ export declare class DefaultFeedPlayerBuffer<T extends WithId> implements IFeedPlayerBuffer<T> {
3
+ readonly kind = "feed";
4
+ readonly loaded: (T & {
5
+ mediaIndex?: number;
6
+ })[];
7
+ readonly currentIndex: number;
8
+ readonly current: (T & {
9
+ mediaIndex?: number;
10
+ }) | null;
11
+ readonly canLoadNext: boolean;
12
+ readonly canLoadPrevious: boolean;
13
+ readonly navigationDisabled: boolean;
14
+ readonly animationDuration = 500;
15
+ private _currentIndex;
16
+ private _loaded;
17
+ private _loadMoreFn;
18
+ private _fetchDeferred;
19
+ private _onEndReachedFn;
20
+ constructor(provider: IFeedPlayerDataProvider<T>);
21
+ tryActivateItemById: (id: string) => boolean;
22
+ removeItemById: (id: string) => boolean;
23
+ loadNext: () => Promise<void>;
24
+ loadPrevious: () => Promise<void>;
25
+ reset: () => void;
26
+ ensureWarmedUp: () => Promise<void>;
27
+ private initializeBuffer;
28
+ private warmUpBuffer;
29
+ }
@@ -0,0 +1,121 @@
1
+ import { Deferred } from '../../../core';
2
+ import { Utils } from '../../../core/utils';
3
+ const BUFFER_SIZE = 5;
4
+ export class DefaultFeedPlayerBuffer {
5
+ kind = 'feed';
6
+ loaded = $derived.by(() => this._loaded);
7
+ currentIndex = $derived.by(() => this._currentIndex);
8
+ current = $derived(this.currentIndex >= 0 ? this.loaded[this.currentIndex] : null);
9
+ canLoadNext = $derived.by(() => {
10
+ if (this.loaded.length && this.currentIndex < this.loaded.length - 1) {
11
+ return true;
12
+ }
13
+ return !this._fetchDeferred && !!this._onEndReachedFn;
14
+ });
15
+ canLoadPrevious = $derived(this.currentIndex > 0);
16
+ navigationDisabled = $derived(!this.canLoadNext && !this.canLoadPrevious);
17
+ animationDuration = 500;
18
+ _currentIndex = $state(-1);
19
+ _loaded = $state.raw([]);
20
+ _loadMoreFn;
21
+ _fetchDeferred = $state.raw(null);
22
+ _onEndReachedFn;
23
+ constructor(provider) {
24
+ this._onEndReachedFn = provider.onEndReached;
25
+ this._loadMoreFn = provider.loadMore;
26
+ // Throttle navigation methods
27
+ this.loadNext = Utils.throttle(this.loadNext, this.animationDuration);
28
+ this.loadPrevious = Utils.throttle(this.loadPrevious, this.animationDuration);
29
+ this.initializeBuffer({ initialData: provider.initialData });
30
+ }
31
+ tryActivateItemById = (id) => {
32
+ const itemIndex = this._loaded.findIndex((item) => item.id === id);
33
+ if (itemIndex === -1) {
34
+ return false;
35
+ }
36
+ this._currentIndex = itemIndex;
37
+ return true;
38
+ };
39
+ removeItemById = (id) => {
40
+ const itemIndex = this._loaded.findIndex((item) => item.id === id);
41
+ if (itemIndex === -1) {
42
+ return false;
43
+ }
44
+ this._loaded = this._loaded.filter((item) => item.id !== id);
45
+ if (itemIndex < this._currentIndex) {
46
+ this._currentIndex--;
47
+ }
48
+ else if (itemIndex === this._currentIndex) {
49
+ if (this._currentIndex >= this._loaded.length) {
50
+ this._currentIndex = this._loaded.length - 1;
51
+ }
52
+ // Otherwise keep the same index (activates next item)
53
+ }
54
+ return true;
55
+ };
56
+ loadNext = async () => {
57
+ if (!this.canLoadNext) {
58
+ return;
59
+ }
60
+ if (this.currentIndex < this.loaded.length - 1) {
61
+ ++this._currentIndex;
62
+ await this.warmUpBuffer();
63
+ return;
64
+ }
65
+ this._onEndReachedFn?.();
66
+ };
67
+ loadPrevious = async () => {
68
+ if (!this.canLoadPrevious) {
69
+ return;
70
+ }
71
+ --this._currentIndex;
72
+ };
73
+ reset = () => {
74
+ this._loaded = [];
75
+ this._currentIndex = -1;
76
+ this._fetchDeferred = null;
77
+ this.warmUpBuffer();
78
+ };
79
+ ensureWarmedUp = async () => {
80
+ await this.warmUpBuffer();
81
+ };
82
+ initializeBuffer = async (options) => {
83
+ const { initialData } = options;
84
+ const handleInitialized = () => {
85
+ const startIndex = initialData.startIndex >= 0 ? initialData.startIndex : 0;
86
+ this._currentIndex = Math.min(startIndex, this._loaded.length - 1);
87
+ const hasCustomMediaIndex = initialData.startMediaIndex && initialData.startMediaIndex > 0;
88
+ const activeItem = this._loaded[this._currentIndex];
89
+ if (activeItem && hasCustomMediaIndex) {
90
+ activeItem.mediaIndex = initialData.startMediaIndex;
91
+ }
92
+ };
93
+ this._loaded = initialData.prefetchedItems;
94
+ const considerInitialized = this._loaded.length > 0;
95
+ if (considerInitialized) {
96
+ handleInitialized();
97
+ }
98
+ await this.warmUpBuffer();
99
+ if (!considerInitialized) {
100
+ handleInitialized();
101
+ }
102
+ };
103
+ warmUpBuffer = async () => {
104
+ // Early return if buffer is sufficient or already loading
105
+ if (this._fetchDeferred) {
106
+ return this._fetchDeferred.promise;
107
+ }
108
+ if (this._loaded.length >= this.currentIndex + BUFFER_SIZE) {
109
+ return;
110
+ }
111
+ this._fetchDeferred = new Deferred();
112
+ try {
113
+ const result = await this._loadMoreFn();
114
+ this._loaded = [...this._loaded, ...result];
115
+ }
116
+ finally {
117
+ this._fetchDeferred.resolve();
118
+ this._fetchDeferred = null;
119
+ }
120
+ };
121
+ }
@@ -0,0 +1,4 @@
1
+ export { DefaultChunksPlayerBuffer } from './default-chunks-player-buffer.svelte';
2
+ export { DefaultFeedPlayerBuffer } from './default-feed-player-buffer.svelte';
3
+ export { initBufferFromProvider } from './service';
4
+ export type { IChunksPlayerDataProvider, IFeedPlayerDataProvider, IPlayerBuffer, IPlayerDataProvider } from './types';
@@ -0,0 +1,3 @@
1
+ export { DefaultChunksPlayerBuffer } from './default-chunks-player-buffer.svelte';
2
+ export { DefaultFeedPlayerBuffer } from './default-feed-player-buffer.svelte';
3
+ export { initBufferFromProvider } from './service';
@@ -0,0 +1,2 @@
1
+ import type { IPlayerBuffer, IPlayerDataProvider, WithId } from './types';
2
+ export declare const initBufferFromProvider: <T extends WithId>(provider: IPlayerDataProvider<T>) => IPlayerBuffer<T>;
@@ -0,0 +1,13 @@
1
+ import { Utils } from '../../../core/utils';
2
+ import { DefaultChunksPlayerBuffer } from './default-chunks-player-buffer.svelte';
3
+ import { DefaultFeedPlayerBuffer } from './default-feed-player-buffer.svelte';
4
+ export const initBufferFromProvider = (provider) => {
5
+ switch (provider.kind) {
6
+ case 'feed':
7
+ return new DefaultFeedPlayerBuffer(provider);
8
+ case 'chunks':
9
+ return new DefaultChunksPlayerBuffer(provider);
10
+ default:
11
+ Utils.assertUnreachable(provider);
12
+ }
13
+ };
@@ -0,0 +1,54 @@
1
+ export type IPlayerDataProvider<T extends WithId> = IFeedPlayerDataProvider<T> | IChunksPlayerDataProvider<T>;
2
+ export interface IFeedPlayerDataProvider<T extends WithId> {
3
+ kind: 'feed';
4
+ initialData: {
5
+ prefetchedItems: T[];
6
+ startIndex: number;
7
+ startMediaIndex?: number;
8
+ };
9
+ loadMore: () => Promise<T[]>;
10
+ onEndReached?: () => void;
11
+ }
12
+ export interface IChunksPlayerDataProvider<TItem extends WithId, TChunk extends WithId = WithId> {
13
+ kind: 'chunks';
14
+ initialData: {
15
+ prefetchedChunks: TChunk[];
16
+ startItemIndex?: number;
17
+ };
18
+ loadMoreChunks: () => Promise<TChunk[]>;
19
+ loadChunkItems: (chunkId: string, continuationToken: string | null | undefined) => Promise<{
20
+ items: TItem[];
21
+ continuationToken: string | null;
22
+ }>;
23
+ onEndReached?: () => void;
24
+ }
25
+ export type IPlayerBuffer<T extends WithId> = IFeedPlayerBuffer<T> | IChunksPlayerBuffer<T>;
26
+ export interface IPlayerBufferBase<T extends WithId> {
27
+ readonly kind: 'feed' | 'chunks';
28
+ readonly current: TExtended<T> | null;
29
+ readonly loaded: TExtended<T>[];
30
+ readonly currentIndex: number;
31
+ readonly canLoadNext: boolean;
32
+ readonly canLoadPrevious: boolean;
33
+ readonly navigationDisabled: boolean;
34
+ readonly animationDuration: number;
35
+ loadNext: () => void;
36
+ loadPrevious: () => void;
37
+ reset: () => void;
38
+ ensureWarmedUp: () => Promise<void>;
39
+ tryActivateItemById: (id: string) => boolean;
40
+ removeItemById: (id: string) => void;
41
+ }
42
+ type TExtended<T> = T & {
43
+ mediaIndex?: number;
44
+ };
45
+ export interface IFeedPlayerBuffer<T extends WithId> extends IPlayerBufferBase<T> {
46
+ readonly kind: 'feed';
47
+ }
48
+ export interface IChunksPlayerBuffer<T extends WithId> extends IPlayerBufferBase<T> {
49
+ readonly kind: 'chunks';
50
+ }
51
+ export type WithId = {
52
+ id: string;
53
+ };
54
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export { TouchSynchronizer } from './touch-synchronizer';
@@ -0,0 +1 @@
1
+ export { TouchSynchronizer } from './touch-synchronizer';
@@ -0,0 +1,7 @@
1
+ export declare class TouchSynchronizer {
2
+ private static listeners;
3
+ static touchStarted: (ref: HTMLElement) => void;
4
+ static touchMovePropagationBlocked: (ref: HTMLElement) => void;
5
+ static touchEndPropatationBlocked: (ref: HTMLElement) => void;
6
+ static touchEnded: (ref: HTMLElement) => void;
7
+ }
@@ -0,0 +1,21 @@
1
+ export class TouchSynchronizer {
2
+ static listeners = [];
3
+ static touchStarted = (ref) => {
4
+ TouchSynchronizer.listeners.push(ref);
5
+ };
6
+ static touchMovePropagationBlocked = (ref) => {
7
+ const otherListeners = TouchSynchronizer.listeners.filter((r) => r !== ref);
8
+ otherListeners.forEach((r) => {
9
+ r.dispatchEvent(new TouchEvent('touchend', { cancelable: false }));
10
+ });
11
+ };
12
+ static touchEndPropatationBlocked = (ref) => {
13
+ const otherListeners = TouchSynchronizer.listeners.filter((r) => r !== ref);
14
+ otherListeners.forEach((r) => {
15
+ r.dispatchEvent(new TouchEvent('touchend', { cancelable: false }));
16
+ });
17
+ };
18
+ static touchEnded = (ref) => {
19
+ TouchSynchronizer.listeners = TouchSynchronizer.listeners.filter((r) => r !== ref);
20
+ };
21
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@streamscloud/kit",
3
- "version": "0.0.1-1771006476137",
3
+ "version": "0.0.1-1771075495185",
4
4
  "author": "StreamsCloud",
5
5
  "repository": {
6
6
  "type": "git",
@@ -128,6 +128,22 @@
128
128
  "types": "./dist/ui/media-playback/index.d.ts",
129
129
  "svelte": "./dist/ui/media-playback/index.js"
130
130
  },
131
+ "./ui/player/buttons": {
132
+ "types": "./dist/ui/player/buttons/index.d.ts",
133
+ "svelte": "./dist/ui/player/buttons/index.js"
134
+ },
135
+ "./ui/player/carousel": {
136
+ "types": "./dist/ui/player/carousel/index.d.ts",
137
+ "svelte": "./dist/ui/player/carousel/index.js"
138
+ },
139
+ "./ui/player/feed-slider": {
140
+ "types": "./dist/ui/player/feed-slider/index.d.ts",
141
+ "svelte": "./dist/ui/player/feed-slider/index.js"
142
+ },
143
+ "./ui/player/providers": {
144
+ "types": "./dist/ui/player/providers/index.d.ts",
145
+ "svelte": "./dist/ui/player/providers/index.js"
146
+ },
131
147
  "./ui/progress": {
132
148
  "types": "./dist/ui/progress/index.d.ts",
133
149
  "svelte": "./dist/ui/progress/index.js"
@@ -184,12 +200,13 @@
184
200
  "rfdc": "^1.4.1",
185
201
  "svelte": "^5.50.0",
186
202
  "svelte-sonner": "^1.0.7",
203
+ "wheel-gestures": "^2.2.48",
187
204
  "yup": "^1.7.1"
188
205
  },
189
206
  "devDependencies": {
190
207
  "@eslint/compat": "^2.0.2",
191
208
  "@eslint/js": "^9.39.2",
192
- "@fluentui/svg-icons": "^1.1.318",
209
+ "@fluentui/svg-icons": "^1.1.319",
193
210
  "@popperjs/core": "^2.11.8",
194
211
  "@sveltejs/package": "^2.5.7",
195
212
  "@sveltejs/vite-plugin-svelte": "^6.2.4",
@@ -217,14 +234,15 @@
217
234
  "publint": "^0.3.17",
218
235
  "rfdc": "^1.4.1",
219
236
  "sass": "^1.97.3",
220
- "svelte": "^5.50.1",
221
- "svelte-check": "^4.3.6",
237
+ "svelte": "^5.51.0",
238
+ "svelte-check": "^4.4.0",
222
239
  "svelte-preprocess": "^6.0.3",
223
240
  "svelte-sonner": "^1.0.7",
224
241
  "typescript": "^5.9.3",
225
242
  "typescript-eslint": "^8.55.0",
226
243
  "vite": "^7.3.1",
227
- "vite-tsconfig-paths": "^6.1.0",
244
+ "vite-tsconfig-paths": "^6.1.1",
245
+ "wheel-gestures": "^2.2.48",
228
246
  "yup": "^1.7.1"
229
247
  }
230
248
  }