@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,38 @@
1
+ import type { CarouselMode } from './types';
2
+ import { type Snippet } from 'svelte';
3
+ declare function $$render<T>(): {
4
+ props: {
5
+ items: T[];
6
+ initialIndex: number;
7
+ mode?: CarouselMode;
8
+ autoSlideMs?: number;
9
+ on?: {
10
+ indexChanged: (index: number) => void;
11
+ };
12
+ children: Snippet<[T]>;
13
+ dot?: Snippet<[{
14
+ active: boolean;
15
+ }]>;
16
+ };
17
+ exports: {};
18
+ bindings: "";
19
+ slots: {};
20
+ events: {};
21
+ };
22
+ declare class __sveltets_Render<T> {
23
+ props(): ReturnType<typeof $$render<T>>['props'];
24
+ events(): ReturnType<typeof $$render<T>>['events'];
25
+ slots(): ReturnType<typeof $$render<T>>['slots'];
26
+ bindings(): "";
27
+ exports(): {};
28
+ }
29
+ interface $$IsomorphicComponent {
30
+ new <T>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
31
+ $$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
32
+ } & ReturnType<__sveltets_Render<T>['exports']>;
33
+ <T>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
34
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
35
+ }
36
+ declare const Cmp: $$IsomorphicComponent;
37
+ type Cmp<T> = InstanceType<typeof Cmp<T>>;
38
+ export default Cmp;
@@ -0,0 +1,2 @@
1
+ export { default as Carousel } from './cmp.carousel.svelte';
2
+ export type { CarouselMode } from './types';
@@ -0,0 +1 @@
1
+ export { default as Carousel } from './cmp.carousel.svelte';
@@ -0,0 +1 @@
1
+ export type CarouselMode = 'arrows-and-dots' | 'arrows-only' | 'dots-only' | 'dots-only-below' | 'arrows-with-counts';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,206 @@
1
+ <script lang="ts" generics="T extends { id: string }">import { TouchSynchronizer } from '../utils';
2
+ import { createWheelAdapter } from './wheel-gestures-adapter';
3
+ import { onDestroy, onMount, untrack } from 'svelte';
4
+ let { buffer, on, children } = $props();
5
+ let slidesRef;
6
+ let sliderHeight = $state(0);
7
+ let swipeTransition = $state(0);
8
+ let cssAnimationDuration = $derived(buffer.animationDuration * 0.55);
9
+ let resizeObserver;
10
+ let sliderIndex = $state.raw(untrack(() => buffer.currentIndex));
11
+ const actualTransition = $derived(-sliderHeight * sliderIndex);
12
+ const onKeyPress = (e) => {
13
+ if (e.key === 'ArrowUp' || e.key === 'PageUp') {
14
+ buffer.loadPrevious();
15
+ }
16
+ if (e.key === 'ArrowDown' || e.key === 'PageDown') {
17
+ buffer.loadNext();
18
+ }
19
+ };
20
+ $effect(() => {
21
+ if (buffer.currentIndex >= 0 && sliderIndex >= 0 && Math.abs(sliderIndex - buffer.currentIndex) === 1) {
22
+ slidesRef.classList.toggle('animate', true);
23
+ }
24
+ else if (buffer.currentIndex >= 0 && (Math.abs(sliderIndex - buffer.currentIndex) !== 0 || !active)) {
25
+ untrack(() => notifyOnItemChanged());
26
+ }
27
+ });
28
+ let active = null;
29
+ $effect(() => {
30
+ sliderIndex = buffer.currentIndex;
31
+ });
32
+ const notifyOnItemChanged = () => {
33
+ if (active && buffer.current?.id !== active.id) {
34
+ on?.itemDeactivated?.(active.id);
35
+ }
36
+ active = buffer.current;
37
+ if (active) {
38
+ on?.itemActivated?.(active.id);
39
+ }
40
+ };
41
+ onMount(() => {
42
+ let touchStartX = 0;
43
+ let touchStartY = 0;
44
+ let touchMoveY = 0;
45
+ let touchStartTime = 0;
46
+ let swipeState = 'not-initialized';
47
+ window.addEventListener(`keydown`, onKeyPress);
48
+ slidesRef.addEventListener('touchstart', (e) => {
49
+ TouchSynchronizer.touchStarted(slidesRef);
50
+ // The movement gets all janky if there's a transition on the elements.
51
+ slidesRef.classList.toggle('animate', false);
52
+ touchStartX = e.changedTouches[0].screenX;
53
+ touchStartY = e.changedTouches[0].screenY;
54
+ touchStartTime = Date.now();
55
+ swipeState = 'not-initialized';
56
+ }, { passive: true });
57
+ slidesRef.addEventListener('touchmove', (e) => {
58
+ if (swipeState === 'vertical') {
59
+ // Already determined this is a vertical swipe
60
+ e.preventDefault();
61
+ e.stopPropagation();
62
+ TouchSynchronizer.touchMovePropagationBlocked(slidesRef);
63
+ }
64
+ else if (swipeState === 'horizontal') {
65
+ // Already determined this is a horizontal swipe
66
+ return;
67
+ }
68
+ const touchCurrentX = e.changedTouches[0].screenX;
69
+ const touchCurrentY = e.changedTouches[0].screenY;
70
+ const diffX = Math.abs(touchCurrentX - touchStartX);
71
+ const diffY = Math.abs(touchCurrentY - touchStartY);
72
+ // Determine direction on first significant movement
73
+ if (diffX > 10 || diffY > 10) {
74
+ if (diffY > diffX) {
75
+ // Vertical swipe - handle it
76
+ swipeState = 'vertical';
77
+ e.preventDefault();
78
+ e.stopPropagation();
79
+ TouchSynchronizer.touchMovePropagationBlocked(slidesRef);
80
+ const newPosition = touchCurrentY;
81
+ const diff = newPosition - touchStartY;
82
+ if ((diff > 0 && buffer.canLoadPrevious) || (diff < 0 && buffer.canLoadNext)) {
83
+ touchMoveY = diff;
84
+ swipeTransition = diff + actualTransition;
85
+ }
86
+ }
87
+ else {
88
+ // Horizontal swipe - allow child to handle
89
+ swipeState = 'horizontal';
90
+ return;
91
+ }
92
+ }
93
+ }, { passive: false });
94
+ slidesRef.addEventListener('touchend', (e) => {
95
+ const reset = () => {
96
+ swipeTransition = 0;
97
+ touchMoveY = 0;
98
+ };
99
+ // Check if this is a vertical swipe
100
+ if (swipeState !== 'vertical') {
101
+ // Horizontal swipe - don't handle and don't block
102
+ reset();
103
+ return;
104
+ }
105
+ // This is a vertical swipe - block propagation
106
+ e.stopPropagation();
107
+ TouchSynchronizer.touchEndPropatationBlocked(slidesRef);
108
+ TouchSynchronizer.touchEnded(slidesRef);
109
+ slidesRef.classList.toggle('animate', true);
110
+ const isFastSwipe = Date.now() - touchStartTime < 300;
111
+ if (!touchMoveY || (!isFastSwipe && Math.abs(touchMoveY) < sliderHeight / 6)) {
112
+ return reset();
113
+ }
114
+ if (touchMoveY > 0) {
115
+ buffer.loadPrevious();
116
+ }
117
+ else {
118
+ buffer.loadNext();
119
+ }
120
+ reset();
121
+ });
122
+ slidesRef.addEventListener('transitionend', (e) => {
123
+ if (e.target !== slidesRef) {
124
+ return;
125
+ }
126
+ slidesRef.classList.toggle('animate', false);
127
+ notifyOnItemChanged();
128
+ });
129
+ sliderHeight = slidesRef.clientHeight;
130
+ resizeObserver = new ResizeObserver(() => {
131
+ sliderHeight = slidesRef.clientHeight;
132
+ });
133
+ resizeObserver.observe(slidesRef);
134
+ });
135
+ onDestroy(() => {
136
+ if (resizeObserver) {
137
+ resizeObserver.disconnect();
138
+ }
139
+ window.removeEventListener('keydown', onKeyPress);
140
+ });
141
+ const wheelCallbacks = {
142
+ canLoadNext: () => buffer.canLoadNext,
143
+ canLoadPrevious: () => buffer.canLoadPrevious,
144
+ onTrigger: (direction) => {
145
+ // direction: 1 -> next, -1 -> previous
146
+ if (direction > 0) {
147
+ buffer.loadNext();
148
+ }
149
+ else if (direction < 0) {
150
+ buffer.loadPrevious();
151
+ }
152
+ },
153
+ getAnimationDurationMs: () => buffer.animationDuration
154
+ };
155
+ </script>
156
+
157
+ <div class="feed-slider">
158
+ <div
159
+ class="feed-slider__slides"
160
+ bind:this={slidesRef}
161
+ use:createWheelAdapter={{ cbs: wheelCallbacks }}
162
+ style:transform="translateY({swipeTransition || actualTransition}px)"
163
+ style:--feed-slider--animation="{cssAnimationDuration}ms ease-out transform">
164
+ {#each buffer.loaded as item, index (item)}
165
+ <div class="feed-slider__slide">
166
+ {#if index >= sliderIndex - 1 && index <= sliderIndex + 1}
167
+ <!-- Only render the active slide and its immediate neighbors for performance -->
168
+ {@render children({ item, active: index === sliderIndex })}
169
+ {/if}
170
+ </div>
171
+ {/each}
172
+ </div>
173
+ </div>
174
+
175
+ <style>.feed-slider {
176
+ position: relative;
177
+ overflow: clip;
178
+ height: 100%;
179
+ width: 100%;
180
+ min-width: 100%;
181
+ max-width: 100%;
182
+ }
183
+ .feed-slider__slides {
184
+ --_feed-slider--animation: var(--feed-slider--animation);
185
+ height: 100%;
186
+ min-height: 100%;
187
+ max-height: 100%;
188
+ width: 100%;
189
+ min-width: 100%;
190
+ max-width: 100%;
191
+ }
192
+ .feed-slider__slides:global(.animate) {
193
+ transition: var(--_feed-slider--animation);
194
+ }
195
+ .feed-slider__slide {
196
+ display: flex;
197
+ justify-content: center;
198
+ align-items: center;
199
+ position: relative;
200
+ height: 100%;
201
+ min-height: 100%;
202
+ max-height: 100%;
203
+ width: 100%;
204
+ min-width: 100%;
205
+ max-width: 100%;
206
+ }</style>
@@ -0,0 +1,43 @@
1
+ import type { FeedSliderBuffer, FeedSliderCallbacks } from './types';
2
+ import { type Snippet } from 'svelte';
3
+ declare function $$render<T extends {
4
+ id: string;
5
+ }>(): {
6
+ props: {
7
+ buffer: FeedSliderBuffer<T>;
8
+ on?: FeedSliderCallbacks;
9
+ children: Snippet<[{
10
+ item: T;
11
+ active?: boolean;
12
+ }]>;
13
+ };
14
+ exports: {};
15
+ bindings: "";
16
+ slots: {};
17
+ events: {};
18
+ };
19
+ declare class __sveltets_Render<T extends {
20
+ id: string;
21
+ }> {
22
+ props(): ReturnType<typeof $$render<T>>['props'];
23
+ events(): ReturnType<typeof $$render<T>>['events'];
24
+ slots(): ReturnType<typeof $$render<T>>['slots'];
25
+ bindings(): "";
26
+ exports(): {};
27
+ }
28
+ interface $$IsomorphicComponent {
29
+ new <T extends {
30
+ id: string;
31
+ }>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
32
+ $$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
33
+ } & ReturnType<__sveltets_Render<T>['exports']>;
34
+ <T extends {
35
+ id: string;
36
+ }>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
37
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
38
+ }
39
+ declare const Cmp: $$IsomorphicComponent;
40
+ type Cmp<T extends {
41
+ id: string;
42
+ }> = InstanceType<typeof Cmp<T>>;
43
+ export default Cmp;
@@ -0,0 +1,3 @@
1
+ export { default as FeedSlider } from './cmp.feed-slider.svelte';
2
+ export { preventFeedScroll } from './prevent-feed-scroll';
3
+ export type { FeedSliderBuffer, FeedSliderCallbacks } from './types';
@@ -0,0 +1,2 @@
1
+ export { default as FeedSlider } from './cmp.feed-slider.svelte';
2
+ export { preventFeedScroll } from './prevent-feed-scroll';
@@ -0,0 +1,5 @@
1
+ /** @type {import('svelte/action').Action} */
2
+ export declare const preventFeedScroll: (node: HTMLElement, value?: boolean) => {
3
+ destroy(): void;
4
+ };
5
+ export declare const isScrollingPrevented: (node: HTMLElement) => boolean;
@@ -0,0 +1,11 @@
1
+ /** @type {import('svelte/action').Action} */
2
+ export const preventFeedScroll = (node, value = true) => {
3
+ node.dataset[preventFeedScrollingAttribute] = value.toString();
4
+ return {
5
+ destroy() {
6
+ // the node has been removed from the DOM
7
+ }
8
+ };
9
+ };
10
+ export const isScrollingPrevented = (node) => node.dataset[preventFeedScrollingAttribute] === 'true';
11
+ const preventFeedScrollingAttribute = 'preventFeedScrolling';
@@ -0,0 +1,16 @@
1
+ export type FeedSliderCallbacks = {
2
+ itemActivated?: (e: string) => void;
3
+ itemDeactivated?: (e: string) => void;
4
+ };
5
+ export interface FeedSliderBuffer<T extends {
6
+ id: string;
7
+ }> {
8
+ readonly current: T | null;
9
+ readonly loaded: T[];
10
+ readonly currentIndex: number;
11
+ readonly canLoadNext: boolean;
12
+ readonly canLoadPrevious: boolean;
13
+ readonly animationDuration: number;
14
+ loadNext: () => void;
15
+ loadPrevious: () => void;
16
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,17 @@
1
+ export type WheelAdapterCallbacks = {
2
+ canLoadNext: () => boolean;
3
+ canLoadPrevious: () => boolean;
4
+ onTrigger: (direction: number) => void;
5
+ getAnimationDurationMs: () => number;
6
+ };
7
+ /**
8
+ * Minimal, robust wheel adapter:
9
+ * - EMA over axisVelocity[1] to smooth noisy streams (esp. on Windows)
10
+ * - Cooldown blocks triggers while animation runs
11
+ * - Mouse fallback: if velocity is near zero but delta is large, treat as a discrete "kick"
12
+ */
13
+ export declare const createWheelAdapter: (target: HTMLElement, params: {
14
+ cbs: WheelAdapterCallbacks;
15
+ }) => {
16
+ destroy(): void;
17
+ };
@@ -0,0 +1,79 @@
1
+ // wheel-gestures-adapter.ts
2
+ import { WheelGestures } from 'wheel-gestures';
3
+ /**
4
+ * Minimal, robust wheel adapter:
5
+ * - EMA over axisVelocity[1] to smooth noisy streams (esp. on Windows)
6
+ * - Cooldown blocks triggers while animation runs
7
+ * - Mouse fallback: if velocity is near zero but delta is large, treat as a discrete "kick"
8
+ */
9
+ export const createWheelAdapter = (target, params) => {
10
+ const { cbs } = params;
11
+ // Tunables
12
+ const PEAK_THRESHOLD = 0.4; // EMA magnitude threshold to consider as a "peak"
13
+ const ACCEL_THRESHOLD = 0.02; // minimal directional EMA rise to count as real acceleration
14
+ const EMA_ALPHA = 0.35; // EMA smoothing factor (0..1); higher = snappier, noisier
15
+ const MOUSE_DELTA_KICK = 12; // mouse: large delta step (≈12 mac / ≈100 win)
16
+ const MOUSE_STEP_RATIO_KICK = 350; // dimensionless; tune 200–500 if needed
17
+ const wheelGestures = WheelGestures({ preventWheelAction: true, reverseSign: false });
18
+ wheelGestures.observe(target);
19
+ let isAnimating = false;
20
+ let cooldownTimer = null;
21
+ // EMA state
22
+ let emaVelocity = 0;
23
+ let previousEmaVelocity = 0;
24
+ const startCooldown = () => {
25
+ if (cooldownTimer) {
26
+ clearTimeout(cooldownTimer);
27
+ }
28
+ cooldownTimer = window.setTimeout(() => {
29
+ isAnimating = false;
30
+ }, cbs.getAnimationDurationMs() + 100);
31
+ };
32
+ const fire = (direction) => {
33
+ // Respect external guards (e.g., paging boundaries)
34
+ if ((direction > 0 && !cbs.canLoadNext()) || (direction < 0 && !cbs.canLoadPrevious())) {
35
+ return;
36
+ }
37
+ isAnimating = true;
38
+ cbs.onTrigger(direction);
39
+ startCooldown();
40
+ };
41
+ wheelGestures.on('wheel', ({ axisDelta: [, axisDeltaY], axisVelocity: [, axisVelocityY] }) => {
42
+ const velocityY = axisVelocityY || 0;
43
+ const deltaY = axisDeltaY || 0;
44
+ // Tracking only: always update EMA and compute signs/acceleration
45
+ previousEmaVelocity = emaVelocity;
46
+ emaVelocity += (velocityY - emaVelocity) * EMA_ALPHA;
47
+ const emaMagnitude = Math.abs(emaVelocity);
48
+ const velocitySign = Math.sign(emaVelocity) || Math.sign(velocityY);
49
+ const emaAcceleration = emaVelocity - previousEmaVelocity;
50
+ // During animation we only track; no arming, no triggering
51
+ if (isAnimating) {
52
+ return;
53
+ }
54
+ // Path 1: mouse-like discrete kick (platform-agnostic via delta/velocity ratio)
55
+ const absDelta = Math.abs(deltaY);
56
+ const absVel = Math.abs(velocityY);
57
+ const stepRatio = absDelta / Math.max(1e-6, absVel);
58
+ const isMouseLikeKick = absDelta >= MOUSE_DELTA_KICK && stepRatio >= MOUSE_STEP_RATIO_KICK;
59
+ if (isMouseLikeKick) {
60
+ const direction = deltaY > 0 ? 1 : -1; // 1 = next/down, -1 = previous/up
61
+ fire(direction);
62
+ return;
63
+ }
64
+ // Path 2: trackpad/inertia via EMA acceleration and peak gating
65
+ const isAcceleratingInDirection = velocitySign !== 0 && emaAcceleration * velocitySign > ACCEL_THRESHOLD;
66
+ if (isAcceleratingInDirection && emaMagnitude > PEAK_THRESHOLD) {
67
+ const direction = velocitySign > 0 ? 1 : -1; // 1 = next/down, -1 = previous/up
68
+ fire(direction);
69
+ }
70
+ });
71
+ return {
72
+ destroy() {
73
+ wheelGestures.unobserve(target);
74
+ if (cooldownTimer) {
75
+ clearTimeout(cooldownTimer);
76
+ }
77
+ }
78
+ };
79
+ };
@@ -0,0 +1,2 @@
1
+ export { PlayerChunk } from './player-chunk.svelte';
2
+ export { PlayerChunksManager } from './player-chunks-manager.svelte';
@@ -0,0 +1,2 @@
1
+ export { PlayerChunk } from './player-chunk.svelte';
2
+ export { PlayerChunksManager } from './player-chunks-manager.svelte';
@@ -0,0 +1,9 @@
1
+ import type { WithId } from '../types';
2
+ export declare class PlayerChunkItem<T extends WithId> {
3
+ model: T;
4
+ chunkId: string;
5
+ constructor(init: {
6
+ model: T;
7
+ chunkId: string;
8
+ });
9
+ }
@@ -0,0 +1,9 @@
1
+ export class PlayerChunkItem {
2
+ model;
3
+ chunkId;
4
+ constructor(init) {
5
+ const { model, chunkId } = init;
6
+ this.model = model;
7
+ this.chunkId = chunkId;
8
+ }
9
+ }
@@ -0,0 +1,30 @@
1
+ import { PlayerChunkItem } from './player-chunk-item.svelte';
2
+ import type { WithId } from '../types';
3
+ export declare class PlayerChunk<TItem extends WithId, TChunk extends WithId> {
4
+ readonly model: TChunk;
5
+ readonly items: TItem[];
6
+ readonly chunkItems: PlayerChunkItem<TItem>[];
7
+ readonly activeChunkItem: PlayerChunkItem<TItem>;
8
+ private _chunkItems;
9
+ private _activeItemIndex;
10
+ private _fetchDeferred;
11
+ private _itemsLoader;
12
+ constructor(data: {
13
+ chunk: TChunk;
14
+ provider: {
15
+ loadChunkItems: (chunkId: string, continuationToken: string | null | undefined) => Promise<{
16
+ items: TItem[];
17
+ continuationToken: string | null;
18
+ }>;
19
+ };
20
+ callbacks?: {
21
+ onChunkFullyLoaded: (chunk: PlayerChunk<TItem, TChunk>) => void;
22
+ };
23
+ });
24
+ get isLoading(): boolean;
25
+ get canLoadMore(): boolean;
26
+ get activeItemIndex(): number;
27
+ loadMore: () => Promise<TItem[]>;
28
+ setActiveItemIndex: (index: number) => Promise<void>;
29
+ mutateChunkItems: (items: PlayerChunkItem<TItem>[]) => void;
30
+ }
@@ -0,0 +1,72 @@
1
+ import { ContinuationToken, Deferred } from '../../../../core';
2
+ import { CursorDataLoader } from '../../../../core/data-loaders';
3
+ import { PlayerChunkItem } from './player-chunk-item.svelte';
4
+ export class PlayerChunk {
5
+ model;
6
+ items = $derived.by(() => this._chunkItems.map((i) => i.model));
7
+ chunkItems = $derived.by(() => this._chunkItems);
8
+ activeChunkItem = $derived.by(() => this._chunkItems[this._activeItemIndex] ?? null);
9
+ _chunkItems = $state.raw([]);
10
+ _activeItemIndex = $state(0);
11
+ _fetchDeferred = $state.raw(null);
12
+ _itemsLoader;
13
+ constructor(data) {
14
+ const { chunk, provider, callbacks } = data;
15
+ this.model = chunk;
16
+ this._itemsLoader = new CursorDataLoader({
17
+ loadPage: async (continuationToken) => {
18
+ if (this._fetchDeferred) {
19
+ return await this._fetchDeferred.promise;
20
+ }
21
+ if (!this.canLoadMore) {
22
+ return null;
23
+ }
24
+ try {
25
+ this._fetchDeferred = new Deferred();
26
+ const itemsResult = await provider.loadChunkItems(this.model.id, continuationToken.toNextChunkString());
27
+ const newItems = itemsResult.items;
28
+ this._chunkItems = [
29
+ ...this._chunkItems,
30
+ ...newItems.map((item) => new PlayerChunkItem({
31
+ model: item,
32
+ chunkId: this.model.id
33
+ }))
34
+ ];
35
+ const continuationTokenResult = ContinuationToken.fromPayload(itemsResult.continuationToken);
36
+ if (!continuationTokenResult.canLoadMore) {
37
+ callbacks?.onChunkFullyLoaded(this);
38
+ }
39
+ const result = {
40
+ items: newItems,
41
+ continuationToken: continuationTokenResult
42
+ };
43
+ this._fetchDeferred.resolve(result);
44
+ return result;
45
+ }
46
+ catch {
47
+ this._fetchDeferred?.resolve(null);
48
+ return null;
49
+ }
50
+ finally {
51
+ this._fetchDeferred = null;
52
+ }
53
+ }
54
+ });
55
+ }
56
+ get isLoading() {
57
+ return !!this._fetchDeferred;
58
+ }
59
+ get canLoadMore() {
60
+ return this._itemsLoader.continuationToken.canLoadMore;
61
+ }
62
+ get activeItemIndex() {
63
+ return this._activeItemIndex;
64
+ }
65
+ loadMore = () => this._itemsLoader.loadMore();
66
+ setActiveItemIndex = async (index) => {
67
+ this._activeItemIndex = index;
68
+ };
69
+ mutateChunkItems = (items) => {
70
+ this._chunkItems = items;
71
+ };
72
+ }
@@ -0,0 +1,23 @@
1
+ import { PlayerChunk } from './player-chunk.svelte';
2
+ import type { IChunksPlayerDataProvider, WithId } from '../types';
3
+ export declare class PlayerChunksManager<TItem extends WithId, TChunk extends WithId> {
4
+ private provider;
5
+ private _activeChunkIndex;
6
+ private _loadedChunks;
7
+ private _warmUpDeferred;
8
+ constructor(provider: IChunksPlayerDataProvider<TItem, TChunk>);
9
+ get activeChunk(): PlayerChunk<TItem, TChunk>;
10
+ get loadedChunks(): PlayerChunk<TItem, TChunk>[];
11
+ get isLoading(): boolean;
12
+ get flattenedChunkItems(): TItem[];
13
+ get flattenedActiveChunkItemIndex(): number;
14
+ tryActivateItemById: (id: string) => boolean;
15
+ removeItemById: (id: string) => boolean;
16
+ removeChunkById: (id: string) => boolean | undefined;
17
+ initialize: () => Promise<void>;
18
+ setActiveChunkIndex: (index: number, chunkItemIndex: number) => Promise<void>;
19
+ activateItemAtFlattenedIndex: (index: number) => Promise<void>;
20
+ warmUp: () => Promise<void>;
21
+ reset: () => Promise<void>;
22
+ private warmUpSequentially;
23
+ }