sparkle-react 0.0.57 → 0.0.59

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.
package/dist/index.d.mts CHANGED
@@ -1,5 +1,6 @@
1
1
  import React, { ReactNode } from 'react';
2
2
  import { z } from 'zod';
3
+ import * as _twurple_api from '@twurple/api';
3
4
 
4
5
  declare const sparkleConfig: z.ZodObject<{
5
6
  userId: z.ZodString;
@@ -72,6 +73,7 @@ type EventTypeMap = {
72
73
  "notification.subscribe": EventSubSubscribeEvent;
73
74
  "notification.subscribe.end": EventSubSubscriptionEndEvent;
74
75
  "notification.subscribe.gift": EventSubSubscriptionGiftEvent;
76
+ "notification.subscription.message": EventSubSubscriptionMessageEvent;
75
77
  "notification.cheer": EventSubCheerEvent;
76
78
  "notification.tips": StreamElementsTipEvent;
77
79
  "notification.raid": EventSubRaidEvent;
@@ -332,8 +334,29 @@ interface EventSubSubscriptionGiftEvent {
332
334
  cumulative_total: number;
333
335
  is_anonymous: boolean;
334
336
  }
337
+ interface EventSubSubscriptionMessageEmote {
338
+ begin: number;
339
+ end: number;
340
+ id: string;
341
+ }
342
+ interface EventSubSubscriptionMessageEvent {
343
+ user_id: string;
344
+ user_login: string;
345
+ user_name: string;
346
+ broadcaster_user_id: string;
347
+ broadcaster_user_login: string;
348
+ broadcaster_user_name: string;
349
+ tier: "1000" | "2000" | "3000";
350
+ message: {
351
+ text: string;
352
+ emotes: EventSubSubscriptionMessageEmote[] | null;
353
+ };
354
+ cumulative_months: number;
355
+ streak_months: number | null;
356
+ duration_months: number;
357
+ }
335
358
 
336
- declare const SparkleHookKeys: readonly ["notification.follow", "notification.cheer", "notification.tips", "notification.subscribe", "notification.subscribe.end", "notification.subscribe.gift", "notification.raid", "chat.message", "stream.online", "stream.offline", "stream.update", "reward.create", "reward.update", "reward.remove", "reward.redemption.claim", "reward.redemption.update", "poll.begin", "poll.progress", "poll.end", "prediction.begin", "prediction.progress", "prediction.lock", "prediction.end", "moderator.add", "moderator.remove", "moderator.ban", "moderator.unban", "moderator.shield_mode.begin"];
359
+ declare const SparkleHookKeys: readonly ["notification.follow", "notification.cheer", "notification.tips", "notification.subscribe", "notification.subscribe.end", "notification.subscribe.gift", "notification.subscription.message", "notification.raid", "chat.message", "stream.online", "stream.offline", "stream.update", "reward.create", "reward.update", "reward.remove", "reward.redemption.claim", "reward.redemption.update", "poll.begin", "poll.progress", "poll.end", "prediction.begin", "prediction.progress", "prediction.lock", "prediction.end", "moderator.add", "moderator.remove", "moderator.ban", "moderator.unban", "moderator.shield_mode.begin"];
337
360
  type SparkleHookKey = (typeof SparkleHookKeys)[number];
338
361
  declare function useHook<T extends keyof EventTypeMap>(hook: T): EventTypeMap[T] | undefined;
339
362
 
@@ -346,12 +369,250 @@ declare function useTask(): {
346
369
  invokeUntil: (taskId: string, delaySeconds: number, payload?: unknown) => Promise<boolean>;
347
370
  };
348
371
 
349
- /** Sous-ensemble lecture seule de `ctx.twitch` (runtime / Monaco). */
372
+ interface Pagination extends ForwardPagination {
373
+ before?: string;
374
+ }
350
375
  type UserId = string | number;
376
+ interface CustomRewardRedemptionFilter {
377
+ newestFirst?: boolean;
378
+ }
379
+ interface PaginatedCustomRewardRedemptionFilter extends CustomRewardRedemptionFilter, ForwardPagination {
380
+ }
381
+ interface Subscription {
382
+ gifterId: string | null;
383
+ gifterName: string | null;
384
+ gifterDisplayName: string | null;
385
+ userId: string;
386
+ userName: string;
387
+ userDisplayName: string;
388
+ isGift: boolean;
389
+ tier: string;
390
+ }
391
+ interface SubscriptionData {
392
+ data: Subscription[];
393
+ cursor: string | null;
394
+ points: number;
395
+ total: number;
396
+ }
397
+ interface ChatChatterData {
398
+ userId: string;
399
+ userName: string;
400
+ userDisplayName: string;
401
+ }
402
+ interface ChatSettings {
403
+ slowModeEnabled: boolean;
404
+ slowModeDelay: number | null;
405
+ followerOnlyModeEnabled: boolean;
406
+ followerOnlyModeDelay: number | null;
407
+ subscriberOnlyModeEnabled: boolean;
408
+ emoteOnlyModeEnabled: boolean;
409
+ uniqueChatModeEnabled: boolean;
410
+ }
411
+ interface PrivilegedChatSettings extends ChatSettings {
412
+ nonModeratorChatDelay: boolean;
413
+ nonModeratorChatDelayDuration: number | null;
414
+ }
415
+ interface CustomReward {
416
+ id: string;
417
+ backgroundColor: string;
418
+ isEnabled: boolean;
419
+ cost: number;
420
+ title: string;
421
+ prompt: string;
422
+ userInputRequired: boolean;
423
+ maxRedemptionsPerStream: number | null;
424
+ maxRedemptionsPerUserPerStream: number | null;
425
+ globalCooldown: number | null;
426
+ isPaused: boolean;
427
+ isInStock: boolean;
428
+ redemptionsThisStream: number | null;
429
+ autoFulfill: boolean;
430
+ cooldownExpiryDate: Date | null;
431
+ }
432
+ interface CustomRewardRedemption {
433
+ id: string;
434
+ userId: string;
435
+ userName: string;
436
+ userDisplayName: string;
437
+ userInput: string;
438
+ isFulfilled: boolean;
439
+ isCanceled: boolean;
440
+ redemptionDate: Date;
441
+ rewardId: string;
442
+ rewardTitle: string;
443
+ rewardPrompt: string;
444
+ rewardCost: number;
445
+ }
446
+ interface BitsLeaderboardEntry {
447
+ amount: number;
448
+ rank: number;
449
+ userDisplayName: string;
450
+ userId: string;
451
+ userName: string;
452
+ }
453
+ interface BitsLeaderboard {
454
+ entries: BitsLeaderboardEntry[];
455
+ totalCount: number;
456
+ }
457
+ type BitsLeaderboardPeriod = "day" | "week" | "month" | "year" | "all";
458
+ interface BitsLeaderboardQuery {
459
+ count?: number;
460
+ period?: BitsLeaderboardPeriod;
461
+ startDate?: Date;
462
+ contextUserId?: string;
463
+ }
464
+ interface Channel {
465
+ id: string;
466
+ name: string;
467
+ displayName: string;
468
+ language: string;
469
+ gameId: string;
470
+ gameName: string;
471
+ title: string;
472
+ delay: number;
473
+ tags: string[];
474
+ contentClassificationLabels: string[];
475
+ isBrandedContent: boolean;
476
+ }
477
+ interface ChannelEditor {
478
+ userId: string;
479
+ userDisplayName: string;
480
+ creationDate: Date;
481
+ }
482
+ interface Clip {
483
+ id: string;
484
+ url: string;
485
+ embedUrl: string;
486
+ creatorId: string;
487
+ creatorName: string;
488
+ videoId: string;
489
+ gameId: string;
490
+ language: string;
491
+ title: string;
492
+ viewCount: number;
493
+ createdAt: Date;
494
+ thumbnailUrl: string;
495
+ }
496
+ interface PaginatedClipFilter extends Pagination {
497
+ startDate?: string;
498
+ endDate?: string;
499
+ isFeatured?: boolean;
500
+ }
501
+ type GoalType = "follower" | "subscription" | "subscription_count" | "new_subscription" | "new_subscription_count";
502
+ interface Goal {
503
+ id: string;
504
+ type: GoalType;
505
+ description: string;
506
+ currentAmount: number;
507
+ targetAmount: number;
508
+ creationDate: Date;
509
+ }
510
+ type StreamType = "live" | "vodcast" | "";
511
+ interface Stream {
512
+ id: string;
513
+ userId: string;
514
+ userName: string;
515
+ userDisplayName: string;
516
+ gameId: string;
517
+ gameName: string;
518
+ type: StreamType;
519
+ title: string;
520
+ viewers: number;
521
+ startDate: Date;
522
+ language: string;
523
+ thumbnailUrl: string;
524
+ tags: string[];
525
+ isMature: boolean;
526
+ }
527
+ interface StreamMarker {
528
+ id: string;
529
+ creationDate: Date;
530
+ description: string;
531
+ positionInSeconds: number;
532
+ url: string;
533
+ }
534
+ interface BlockedTerm {
535
+ id: string;
536
+ creationDate: Date;
537
+ expirationDate: Date;
538
+ updatedDate: Date;
539
+ moderatorId: string;
540
+ text: string;
541
+ }
542
+ interface Poll {
543
+ id: string;
544
+ broadcasterId: string;
545
+ broadcasterName: string;
546
+ broadcasterDisplayName: string;
547
+ title: string;
548
+ isChannelPointsVotingEnabled: boolean;
549
+ channelPointsPerVote: number;
550
+ status: _twurple_api.HelixPollStatus;
551
+ durationInSeconds: number;
552
+ startDate: Date;
553
+ endDate: Date;
554
+ choices: {
555
+ id: string;
556
+ title: string;
557
+ totalVotes: number;
558
+ channelPointsVotes: number;
559
+ }[];
560
+ }
561
+ type PredictionOutcomeColor = "BLUE" | "PINK";
562
+ type PredictionStatus = "ACTIVE" | "RESOLVED" | "CANCELED" | "LOCKED";
563
+ interface Prediction {
564
+ id: string;
565
+ title: string;
566
+ status: PredictionStatus;
567
+ autoLockAfter: number;
568
+ createdAt: Date;
569
+ endedAt: Date | null;
570
+ lockedAt: Date | null;
571
+ outcomes: PredictionOutcome[];
572
+ winningOutcomeId: string | null;
573
+ winningOutcome: PredictionOutcome | null;
574
+ }
575
+ interface Predictor {
576
+ userId: string;
577
+ userName: string;
578
+ userDisplayName: string;
579
+ channelPointsUsed: number;
580
+ channelPointsWon: number | null;
581
+ }
582
+ interface PredictionOutcome {
583
+ id: string;
584
+ title: string;
585
+ users: number;
586
+ totalChannelPoints: number;
587
+ color: PredictionOutcomeColor;
588
+ topPredictors: Predictor[];
589
+ }
590
+ type UserType = "staff" | "admin" | "global_mod" | "";
591
+ type BroadcasterType = "partner" | "affiliate" | "";
592
+ interface User {
593
+ id: string;
594
+ name: string;
595
+ displayName: string;
596
+ description: string;
597
+ type: UserType;
598
+ broadcasterType: BroadcasterType;
599
+ profilePictureUrl: string;
600
+ offlinePlaceholderUrl: string;
601
+ creationDate: Date;
602
+ }
603
+ interface ForwardPagination {
604
+ limit?: number;
605
+ after?: string;
606
+ }
351
607
  interface ForwardPagination {
352
608
  limit?: number;
353
609
  after?: string;
354
610
  }
611
+ /** Sous-ensemble lecture seule de `ctx.twitch` (runtime / Monaco). */
612
+ interface PaginatedResult<T> {
613
+ readonly data: T[];
614
+ cursor?: string;
615
+ }
355
616
  interface PaginatedResult<T> {
356
617
  readonly data: T[];
357
618
  cursor?: string;
@@ -361,84 +622,95 @@ interface PaginatedResultWithTotal<T> {
361
622
  cursor: string;
362
623
  total: number;
363
624
  }
625
+ interface PaginatedResultWithTotal<T> {
626
+ readonly data: T[];
627
+ cursor: string;
628
+ total: number;
629
+ }
364
630
  interface TwitchGetClient {
365
631
  bits: {
366
- getLeaderboard: (params?: {
367
- count?: number;
368
- period?: "day" | "week" | "month" | "all";
369
- startedAt?: Date;
370
- }) => Promise<unknown>;
632
+ getLeaderboard: (params?: BitsLeaderboardQuery) => Promise<BitsLeaderboard>;
371
633
  };
372
634
  channelPoints: {
373
- getCustomRewards: (onlyManageable?: boolean) => Promise<unknown[]>;
374
- getCustomRewardsByIds: (rewardIds: string[]) => Promise<unknown[]>;
375
- getCustomRewardById: (rewardId: string) => Promise<unknown>;
376
- getRedemptionsByIds: (rewardId: string, redemptionIds: string[]) => Promise<unknown[]>;
377
- getRedemptionById: (rewardId: string, redemptionId: string) => Promise<unknown>;
378
- getRedemptions: (rewardId: string, status: string, filter: unknown) => Promise<PaginatedResult<unknown>>;
635
+ getCustomRewards: (onlyManageable?: boolean) => Promise<CustomReward[]>;
636
+ getCustomRewardsByIds: (rewardIds: string[]) => Promise<CustomReward[]>;
637
+ getCustomRewardById: (rewardId: string) => Promise<CustomReward>;
638
+ getRedemptionsByIds: (rewardId: string, redemptionIds: string[]) => Promise<CustomRewardRedemption[]>;
639
+ getRedemptionById: (rewardId: string, redemptionId: string) => Promise<CustomRewardRedemption>;
640
+ getRedemptions: (rewardId: string, status: string, filter: PaginatedCustomRewardRedemptionFilter) => Promise<PaginatedResult<CustomRewardRedemption>>;
379
641
  };
380
642
  channel: {
381
- getChannelInfoById: (user: UserId) => Promise<unknown>;
643
+ getChannelInfoById: (user: UserId) => Promise<Channel>;
382
644
  getFollowDate: (user: UserId) => Promise<number | null>;
383
- getChannelEditors: () => Promise<unknown[]>;
384
- getVips: () => Promise<unknown[]>;
385
- checkVipForUsers: (users: UserId[]) => Promise<unknown[]>;
645
+ getChannelEditors: () => Promise<ChannelEditor[]>;
646
+ getVips: () => Promise<ChatChatterData[]>;
647
+ checkVipForUsers: (users: UserId[]) => Promise<ChatChatterData[]>;
386
648
  checkVipForUser: (user: UserId) => Promise<boolean>;
387
649
  getChannelFollowerCount: () => Promise<number>;
388
650
  };
389
651
  chat: {
390
- getChatters: (pagination?: ForwardPagination) => Promise<PaginatedResultWithTotal<unknown>>;
391
- getSettings: () => Promise<unknown>;
392
- getSettingsPrivileged: () => Promise<unknown>;
652
+ getChatters: (pagination?: ForwardPagination) => Promise<PaginatedResultWithTotal<ChatChatterData>>;
653
+ getSettings: () => Promise<ChatSettings>;
654
+ getSettingsPrivileged: () => Promise<PrivilegedChatSettings>;
393
655
  getColorsForUsers: (users: UserId[]) => Promise<Record<string, string>>;
394
656
  getColorForUser: (user: UserId) => Promise<string>;
395
657
  };
396
658
  clips: {
397
- getClips: (filter?: unknown) => Promise<PaginatedResult<unknown>>;
398
- getClipById: (id: string) => Promise<unknown>;
399
- getClipsByIds: (ids: string[]) => Promise<unknown[]>;
659
+ getClips: (filter?: PaginatedClipFilter) => Promise<PaginatedResult<Clip>>;
660
+ getClipById: (id: string) => Promise<Clip>;
661
+ getClipsByIds: (ids: string[]) => Promise<Clip[]>;
400
662
  };
401
663
  goals: {
402
- getGoals: () => Promise<unknown[]>;
664
+ getGoals: () => Promise<Goal[]>;
403
665
  };
404
666
  polls: {
405
667
  getPolls: (pagination?: ForwardPagination) => Promise<{
406
- data: unknown[];
668
+ data: Poll[];
407
669
  cursor: string;
408
670
  }>;
409
- getPollsByIds: (ids: string[]) => Promise<unknown[]>;
410
- getPollById: (id: string) => Promise<unknown>;
671
+ getPollsByIds: (ids: string[]) => Promise<Poll[]>;
672
+ getPollById: (id: string) => Promise<Poll>;
411
673
  };
412
674
  predictions: {
413
675
  getPredictions: (pagination?: ForwardPagination) => Promise<{
414
- data: unknown[];
676
+ data: Prediction[];
415
677
  cursor: string;
416
678
  }>;
417
- getPredictionsByIds: (ids: string[]) => Promise<unknown[]>;
418
- getPredictionById: (id: string) => Promise<unknown>;
679
+ getPredictionsByIds: (ids: string[]) => Promise<Prediction[]>;
680
+ getPredictionById: (id: string) => Promise<Prediction>;
419
681
  };
420
682
  streams: {
421
- getStream: () => Promise<unknown | null>;
422
- getStreamMarkers: (pagination?: ForwardPagination) => Promise<PaginatedResult<unknown>>;
683
+ getStream: () => Promise<Stream | null>;
684
+ getStreamMarkers: (pagination?: ForwardPagination) => Promise<PaginatedResult<StreamMarker>>;
423
685
  };
424
686
  subscriptions: {
425
- getSubscriptions: (pagination?: ForwardPagination) => Promise<unknown>;
426
- getSubscriptionsForUsers: (users: UserId[]) => Promise<unknown[]>;
427
- getSubscriptionForUser: (user: UserId) => Promise<unknown>;
428
- checkUserSubscription: (user: UserId) => Promise<unknown>;
687
+ getSubscriptions: (pagination?: ForwardPagination) => Promise<SubscriptionData>;
688
+ getSubscriptionsForUsers: (users: UserId[]) => Promise<Subscription[]>;
689
+ getSubscriptionForUser: (user: UserId) => Promise<Subscription>;
690
+ checkUserSubscription: (user: UserId) => Promise<Subscription>;
429
691
  };
430
692
  moderation: {
431
- getBanned: () => Promise<unknown[]>;
693
+ getBanned: () => Promise<{
694
+ creationDate: Date;
695
+ expiryDate: Date;
696
+ moderatorDisplayName: string;
697
+ moderatorId: string;
698
+ moderatorName: string;
699
+ reason: string;
700
+ userId: string;
701
+ userName: string;
702
+ userDisplayName: string;
703
+ }[]>;
432
704
  checkBan: (userId: string) => Promise<boolean>;
433
705
  getModerators: () => Promise<unknown[]>;
434
706
  checkModerator: (userId: string) => Promise<boolean>;
435
- getBlockedTerms: () => Promise<unknown[]>;
707
+ getBlockedTerms: () => Promise<BlockedTerm[]>;
436
708
  };
437
709
  utils: {
438
- getUserById: (id: string) => Promise<unknown>;
439
- getUserByName: (name: string) => Promise<unknown>;
440
- getUserByIds: (ids: string[]) => Promise<unknown[]>;
441
- getUserByNames: (names: string[]) => Promise<unknown[]>;
710
+ getUserById: (id: string) => Promise<User>;
711
+ getUserByName: (name: string) => Promise<User>;
712
+ getUserByIds: (ids: string[]) => Promise<User[]>;
713
+ getUserByNames: (names: string[]) => Promise<User[]>;
442
714
  };
443
715
  }
444
716
 
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import y,{useMemo as ue,useEffect as le,useState as de}from"react";import*as x from"react";import{createContext as H}from"react";var S=H({token:null}),L=({children:r,config:e})=>x.createElement(S.Provider,{value:{token:e.userId}},r);import Q,{useCallback as R,useContext as X,useRef as Z}from"react";import{createContext as ee,useEffect as B,useState as U}from"react";import $ from"events";import m from"zod";var K=m.object({type:m.literal("subscribe"),channels:m.array(m.string())}),Y=m.object({type:m.literal("hook"),channels:m.array(m.string())}),J=m.object({type:m.literal("unsubscribe"),channels:m.string()}),ht=m.object({event:m.literal("update"),type:m.enum(["value","hook"]),data:m.object({key:m.string(),value:m.string()})}),yt=m.union([K,J,Y]),b=class extends ${constructor(t){super();this.socket=null;this.queue=[];this.subscriptions=[];this.hooks=[];this.isAuthenticating=!1;this.jwt=null;this.url=t}send(t){this.socket?.readyState===WebSocket.OPEN?this.socket?.send(JSON.stringify(t)):this.queue.push(t)}authenticate(t){this.isAuthenticating||(this.isAuthenticating=!0,this.jwt=t,this.connect())}connect(){let t=this.url+"?jwt="+this.jwt;this.socket=new WebSocket(t),this.socket.onopen=()=>{this.queue.forEach(n=>this.send(n)),this.queue=[]},this.socket.onmessage=n=>{let o=JSON.parse(n.data);this.emit("update",o)},this.socket.onclose=()=>{this.reconnect()}}reconnect(){setTimeout(()=>{this.queue=[{type:"subscribe",channels:this.subscriptions},{type:"hook",channels:this.hooks}],console.log("try reconnecting"),this.connect()},5e3)}subscribe(t){let n=t.filter(o=>!this.subscriptions.includes(o));n.length&&(this.subscriptions=[...this.subscriptions,...n],console.log("subscribe",t),this.send({type:"subscribe",channels:t}))}hook(t){let n=[t].filter(o=>!this.hooks.includes(o));n.length&&(this.hooks=[...this.hooks,...n],this.send({type:"hook",channels:[t]}))}unsubscribe(t){this.send({type:"unsubscribe",channels:t})}disconnect(){this.socket&&(this.socket.onclose=()=>{},this.socket.close())}};var h=ee({subscribe:()=>{},listen:()=>{},values:{},hooks:{}}),M=({children:r,config:e})=>{let[t,n]=U({}),[o,s]=U({}),i=Z(null),{token:u}=X(S);B(()=>(i.current=new b(e.wsUrl),i.current.on("update",d=>{let{data:c,type:g}=d;if(g==="hook"){let f=JSON.parse(c.value);s(p=>({...p,[f.hook]:f.payload})),console.log("receive hooks",f)}else{let f=c.value;console.log("value",f);try{let p=parseFloat(c.value);if(!isNaN(p)&&String(p)===String(c.value).trim())f=p;else try{f=JSON.parse(c.value)}catch{f=c.value}}catch{f=c.value}n(p=>({...p,[c.key]:f})),console.log("receive values",t)}}),()=>{i.current?.disconnect()}),[]),B(()=>{!i.current||!u||i.current.authenticate(u)},[u]);let a=R(d=>{i.current?.subscribe(d)},[i]),l=R(d=>{i.current?.hook(d)},[i]);return Q.createElement(h.Provider,{value:{subscribe:a,listen:l,values:t,hooks:o}},r)};import te,{createContext as re,useCallback as ne,useEffect as oe,useRef as j,useState as se}from"react";var ie=re({status:"disconnected",call:()=>Promise.reject(new Error("ObsProvider not mounted"))});function O({url:r,children:e}){let[t,n]=se("disconnected"),o=j(null),s=j(!1);oe(()=>{if(!r)return;s.current=!1;let u;return(async()=>{let{default:l}=await import("obs-websocket-js"),d=new l;o.current=d;let c=async()=>{if(!s.current){n("connecting");try{await d.connect(r),s.current||n("connected")}catch{s.current||(n("error"),u=setTimeout(c,3e3))}}};d.on("ConnectionClosed",()=>{s.current||(n("disconnected"),u=setTimeout(c,3e3))}),d.on("ConnectionError",()=>{s.current||n("error")}),await c()})(),()=>{s.current=!0,clearTimeout(u),o.current?.disconnect(),o.current=null,n("disconnected")}},[r]);let i=ne(async(u,a)=>{let l=o.current;if(!l)throw new Error("OBS not connected");return l.call(u,a)},[]);return te.createElement(ie.Provider,{value:{status:t,call:i}},e)}import{createContext as ae,useContext as ce}from"react";var I=ae(null);function w(){let r=ce(I);if(!r)throw new Error("Sparkle hooks require SparkleProvider");return r}var pe=({children:r})=>{let[e,t]=de(!1);return le(()=>{t(!0)},[]),e?r:null},fe={userId:"",wsUrl:"wss://sparkle-bot-v2.fly.dev/ws"},Gt=({children:r,config:e})=>{let t={...fe,...e},n=ue(()=>t.obs?.clientId?`${t.obs.bridgeUrl??"wss://sparkle-bot-v2.fly.dev/obs-bridge"}?role=client&clientId=${encodeURIComponent(t.obs.clientId)}`:"",[t.obs?.clientId,t.obs?.bridgeUrl]),o=y.createElement(I.Provider,{value:t},y.createElement(L,{config:t},y.createElement(M,{config:t},y.createElement(pe,null,r))));return n?y.createElement(O,{url:n},o):o};import{useContext as me,useEffect as ge,useMemo as we}from"react";function _t(r,e){let t=me(h),n=we(()=>[r],[r]);if(!t)throw new Error("You must use useRealtime inside a SparkleProvider");return ge(()=>{t.subscribe([r])},[n]),n.map(o=>t.values[o]??e)}import{useEffect as he,useState as E}from"react";var ye="sparkle-widget-session-request",be="sparkle-widget-session-result";function ke(){return typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now()}-${Math.random().toString(16).slice(2)}`}function ve(){if(typeof window>"u")return!1;try{return window.self!==window.top}catch{return!0}}function G(r){if(!r||typeof r!="object")return{userId:null,username:null,profilePicture:null};let e=r,t=typeof e.userId=="string"&&e.userId.trim()?e.userId:null,n=typeof e.username=="string"&&e.username.trim()?e.username:null,o=typeof e.profilePicture=="string"&&e.profilePicture.trim()?e.profilePicture:null;return{userId:t,username:n,profilePicture:o}}function Se(){if(typeof window>"u")return Promise.resolve({userId:null,username:null,profilePicture:null});let r=window.top??window.parent;return!r||r===window?Promise.resolve({userId:null,username:null,profilePicture:null}):new Promise(e=>{let t=ke(),n=s=>{let i=s.data;!i||i.type!==be||i.requestId!==t||(window.removeEventListener("message",n),window.clearTimeout(o),e(G(i)))},o=window.setTimeout(()=>{window.removeEventListener("message",n),e({userId:null,username:null,profilePicture:null})},12e3);window.addEventListener("message",n),r.postMessage({type:ye,requestId:t},"*")})}async function Ie(){let r=await fetch("/api/sparkle-widget-session",{method:"GET",credentials:"include"});if(!r.ok)return{userId:null,username:null,profilePicture:null};let e=await r.json().catch(()=>null);return G(e)}function Wt(){let[r,e]=E(void 0),[t,n]=E(void 0),[o,s]=E(void 0);return he(()=>{let i=!1;return(async()=>{let a=ve()?await Se():await Ie();i||(e(a.userId),n(a.username),s(a.profilePicture))})(),()=>{i=!0}},[]),{userId:r,username:t,profilePicture:o}}import{useContext as Ee,useEffect as Ce}from"react";var Yt=["notification.follow","notification.cheer","notification.tips","notification.subscribe","notification.subscribe.end","notification.subscribe.gift","notification.raid","chat.message","stream.online","stream.offline","stream.update","reward.create","reward.update","reward.remove","reward.redemption.claim","reward.redemption.update","poll.begin","poll.progress","poll.end","prediction.begin","prediction.progress","prediction.lock","prediction.end","moderator.add","moderator.remove","moderator.ban","moderator.unban","moderator.shield_mode.begin"];function Jt(r){let e=Ee(h);if(!e)throw new Error("You must use useHook inside a SparkleProvider");return Ce(()=>{e.listen(r)},[r]),e.hooks[r]}import{useCallback as q,useMemo as Pe}from"react";var Te="sparkle-invoke-task",xe="sparkle-invoke-task-result";function Le(){return typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now()}-${Math.random().toString(16).slice(2)}`}function A(r,e){if(typeof window>"u")return Promise.reject(new Error("useTask requires a browser environment"));let t=window.top??window.parent;return!t||t===window?Promise.reject(new Error("useTask: aucun parent \u2014 utilisez la preview \xE9diteur ou une page int\xE9gr\xE9e.")):new Promise((n,o)=>{let s=Le(),i=a=>{let l=a.data;!l||l.type!==xe||l.requestId!==s||(window.removeEventListener("message",i),window.clearTimeout(u),l.ok?n(!!l.result):o(new Error(typeof l.error=="string"?l.error:"invoke_failed")))},u=window.setTimeout(()=>{window.removeEventListener("message",i),o(new Error("useTask: d\xE9lai d\xE9pass\xE9"))},45e3);window.addEventListener("message",i),t.postMessage({type:Te,requestId:s,projectId:r,...e},"*")})}function D(){if(typeof window>"u")return!1;try{return window.self!==window.top}catch{return!0}}async function N(r,e){let t=await fetch(`/api/projects/${r}/invoke-task`,{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!t.ok){let n=await t.json().catch(()=>null);throw new Error(n?.error??t.statusText)}return!0}function er(){let e=w().projectId,t=q(async(o,s)=>{if(!e?.trim())throw new Error("useTask: renseignez `config.projectId` sur SparkleProvider.");let i={kind:"invoke",taskId:o,payload:s};return D()?A(e,i):N(e,i)},[e]),n=q(async(o,s,i)=>{if(!e?.trim())throw new Error("useTask: renseignez `config.projectId` sur SparkleProvider.");let u={kind:"invokeUntil",taskId:o,payload:i,delaySeconds:s};return D()?A(e,u):N(e,u)},[e]);return Pe(()=>({invoke:t,invokeUntil:n}),[t,n])}import{useCallback as Re,useMemo as Be}from"react";function _(r){let e=(t,n)=>(...o)=>r(t,n,o);return{bits:{getLeaderboard:e("bits","getLeaderboard")},channelPoints:{getCustomRewards:e("channelPoints","getCustomRewards"),getCustomRewardsByIds:e("channelPoints","getCustomRewardsByIds"),getCustomRewardById:e("channelPoints","getCustomRewardById"),getRedemptionsByIds:e("channelPoints","getRedemptionsByIds"),getRedemptionById:e("channelPoints","getRedemptionById"),getRedemptions:e("channelPoints","getRedemptions")},channel:{getChannelInfoById:e("channel","getChannelInfoById"),getFollowDate:e("channel","getFollowDate"),getChannelEditors:e("channel","getChannelEditors"),getVips:e("channel","getVips"),checkVipForUsers:e("channel","checkVipForUsers"),checkVipForUser:e("channel","checkVipForUser"),getChannelFollowerCount:e("channel","getChannelFollowerCount")},chat:{getChatters:e("chat","getChatters"),getSettings:e("chat","getSettings"),getSettingsPrivileged:e("chat","getSettingsPrivileged"),getColorsForUsers:e("chat","getColorsForUsers"),getColorForUser:e("chat","getColorForUser")},clips:{getClips:e("clips","getClips"),getClipById:e("clips","getClipById"),getClipsByIds:e("clips","getClipsByIds")},goals:{getGoals:e("goals","getGoals")},polls:{getPolls:e("polls","getPolls"),getPollsByIds:e("polls","getPollsByIds"),getPollById:e("polls","getPollById")},predictions:{getPredictions:e("predictions","getPredictions"),getPredictionsByIds:e("predictions","getPredictionsByIds"),getPredictionById:e("predictions","getPredictionById")},streams:{getStream:e("streams","getStream"),getStreamMarkers:e("streams","getStreamMarkers")},subscriptions:{getSubscriptions:e("subscriptions","getSubscriptions"),getSubscriptionsForUsers:e("subscriptions","getSubscriptionsForUsers"),getSubscriptionForUser:e("subscriptions","getSubscriptionForUser"),checkUserSubscription:e("subscriptions","checkUserSubscription")},moderation:{getBanned:e("moderation","getBanned"),checkBan:e("moderation","checkBan"),getModerators:e("moderation","getModerators"),checkModerator:e("moderation","checkModerator"),getBlockedTerms:e("moderation","getBlockedTerms")},utils:{getUserById:e("utils","getUserById"),getUserByName:e("utils","getUserByName"),getUserByIds:e("utils","getUserByIds"),getUserByNames:e("utils","getUserByNames")}}}var Ue="sparkle-twitch-get",Me="sparkle-twitch-get-result";function je(){return typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now()}-${Math.random().toString(16).slice(2)}`}function Oe(){if(typeof window>"u")return!1;try{return window.self!==window.top}catch{return!0}}function Ge(r,e){if(typeof window>"u")return Promise.reject(new Error("useTwitch requires a browser environment"));let t=window.top??window.parent;return!t||t===window?Promise.reject(new Error("useTwitch: aucun parent \u2014 utilisez la preview \xE9diteur ou une page int\xE9gr\xE9e.")):new Promise((n,o)=>{let s=je(),i=a=>{let l=a.data;!l||l.type!==Me||l.requestId!==s||(window.removeEventListener("message",i),window.clearTimeout(u),l.ok?n(l.result):o(new Error(typeof l.error=="string"?l.error:"twitch_get_failed")))},u=window.setTimeout(()=>{window.removeEventListener("message",i),o(new Error("useTwitch: d\xE9lai d\xE9pass\xE9"))},45e3);window.addEventListener("message",i),t.postMessage({type:Ue,requestId:s,projectId:r,...e},"*")})}async function qe(r,e){let t=await fetch(`/api/projects/${encodeURIComponent(r)}/twitch-get`,{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!t.ok){let o=await t.json().catch(()=>null);throw new Error(o?.error??t.statusText)}return(await t.json()).result}function ir(){let e=w().projectId,t=Re(async(n,o,s)=>{if(!e?.trim())throw new Error("useTwitch: renseignez `config.projectId` sur SparkleProvider.");let i={path:[n,o],args:s};return Oe()?Ge(e,i):qe(e,i)},[e]);return Be(()=>_(t),[t])}import{useCallback as De,useContext as Ne,useEffect as P,useRef as _e,useState as Fe}from"react";function F(r){if(!r||typeof r!="object")return null;let e=r,t=n=>{let o=Number(e[n]);return Number.isFinite(o)?Math.max(0,Math.floor(o)):0};return{followers:t("followers"),subscribers:t("subscribers"),cheers:t("cheers"),tips:t("tips")}}function Ae(r){if(!r||typeof r!="object")return null;let e=r,t=e.cheers&&typeof e.cheers=="object"?e.cheers:null,n=e.tips&&typeof e.tips=="object"?e.tips:null;return{followers:typeof e.followers=="string"?e.followers:"",subscribers:typeof e.subscribers=="string"?e.subscribers:"",cheers:{amount:t&&Number.isFinite(Number(t.amount))?Math.max(0,Math.floor(Number(t.amount))):0,user:t&&typeof t.user=="string"?t.user:""},tips:{amount:n&&Number.isFinite(Number(n.amount))?Number(n.amount):0,user:n&&typeof n.user=="string"?n.user:""}}}function k(r){return Array.isArray(r)?r.filter(e=>typeof e=="string"):[]}function C(r){if(!r||typeof r!="object")return null;let e=r,t=F(e.session),n=F(e.total),o=Ae(e.latest),s=e.recent&&typeof e.recent=="object"?e.recent:null;return!t||!n||!o||!s?null:{session:t,total:n,latest:o,recent:{followers:k(s.followers),subscribers:k(s.subscribers),cheers:k(s.cheers),tips:k(s.tips)}}}var v=()=>({session:{followers:0,subscribers:0,cheers:0,tips:0},total:{followers:0,subscribers:0,cheers:0,tips:0},latest:{followers:"",subscribers:"",cheers:{amount:0,user:""},tips:{amount:0,user:""}},recent:{followers:[],subscribers:[],cheers:[],tips:[]}});var V="labels:snapshot",Ve="sparkle-labels-get",We="sparkle-labels-get-result";function ze(){return typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now()}-${Math.random().toString(16).slice(2)}`}function He(){if(typeof window>"u")return!1;try{return window.self!==window.top}catch{return!0}}function $e(r){if(typeof window>"u")return Promise.reject(new Error("useLabels requires a browser environment"));let e=window.top??window.parent;return!e||e===window?Promise.reject(new Error("useLabels: aucun parent \u2014 utilisez la preview \xE9diteur ou une page int\xE9gr\xE9e.")):new Promise((t,n)=>{let o=ze(),s=u=>{let a=u.data;!a||a.type!==We||a.requestId!==o||(window.removeEventListener("message",s),window.clearTimeout(i),a.ok&&a.labels?t(a.labels):n(new Error(typeof a.error=="string"?a.error:"labels_get_failed")))},i=window.setTimeout(()=>{window.removeEventListener("message",s),n(new Error("useLabels: d\xE9lai d\xE9pass\xE9"))},45e3);window.addEventListener("message",s),e.postMessage({type:Ve,requestId:o,projectId:r},"*")})}async function Ke(r){let e=await fetch(`/api/projects/${encodeURIComponent(r)}/labels`,{method:"GET",credentials:"include"});if(!e.ok){let n=await e.json().catch(()=>null);throw new Error(n?.error??e.statusText)}return(await e.json()).labels??v()}async function Ye(r,e){return e?$e(r):Ke(r)}function fr(){let r=w(),e=r.projectId?.trim()??"",t=r.userId?.trim()??"",n=Ne(h),o=!!t,s=He()&&!o,[i,u]=Fe(v),a=_e(0),l=n.values[V];P(()=>{o&&n.subscribe([V])},[o,n]),P(()=>{if(!o)return;let c=C(l);c&&u(c)},[o,l]);let d=De(async()=>{if(!e)return;let c=++a.current;try{let g=await Ye(e,s);c===a.current&&u(g)}catch{}},[e,s]);return P(()=>{if(!e){u(v());return}if(o){C(l)||d();return}d()},[e,d,o,l]),i}import{useCallback as Je,useEffect as W,useRef as Qe,useState as T}from"react";function Xe(){return typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now()}-${Math.random().toString(16).slice(2)}`}function Ze(){if(typeof window>"u")return!1;try{return window.self!==window.top}catch{return!0}}function et(r){if(typeof window>"u")return Promise.reject(new Error("useSpotify requires a browser environment"));let e=window.top??window.parent;return!e||e===window?Promise.reject(new Error("useSpotify: aucun parent \u2014 utilisez la preview \xE9diteur ou une page int\xE9gr\xE9e.")):new Promise((t,n)=>{let o=Xe(),s=u=>{let a=u.data;!a||a.type!=="sparkle-spotify-get-result"||a.requestId!==o||(window.removeEventListener("message",s),window.clearTimeout(i),a.ok?t(a.track):n(new Error(typeof a.error=="string"?a.error:"spotify_get_failed")))},i=window.setTimeout(()=>{window.removeEventListener("message",s),n(new Error("useSpotify: d\xE9lai d\xE9pass\xE9"))},45e3);window.addEventListener("message",s),e.postMessage({type:"sparkle-spotify-get",requestId:o,projectId:r},"*")})}async function tt(r){let e=await fetch(`/api/projects/${encodeURIComponent(r)}/spotify`,{method:"GET",credentials:"include"});if(!e.ok){let n=await e.json().catch(()=>null);throw new Error(n?.error??e.statusText)}return(await e.json()).track??null}function hr(r){let t=w().projectId?.trim()??"",n=r?.intervalMs??5e3,o=Ze(),[s,i]=T(null),[u,a]=T(!0),[l,d]=T(null),c=Qe(0),g=Je(async()=>{if(!t){i(null),a(!1);return}let f=++c.current;try{let p=o?await et(t):await tt(t);f===c.current&&(i(p),d(null))}catch(p){f===c.current&&d(p instanceof Error?p:new Error(String(p)))}finally{f===c.current&&a(!1)}},[t,o]);return W(()=>{g()},[g]),W(()=>{if(n<=0||!t)return;let f=setInterval(()=>{g()},n);return()=>clearInterval(f)},[g,n,t]),{track:s,loading:u,error:l,refresh:g}}import{useCallback as rt}from"react";var nt=[{id:"adam",voiceId:"pNInz6obpgDQGcFmaJgB",name:"Adam - Dominant, Firm"},{id:"alice",voiceId:"Xb7hH8MSUJpSbSDYk0k2",name:"Alice - Clear, Engaging Educator"},{id:"andy-lin",voiceId:"h1i3CVVBUuF6s46cxUGG",name:"Andy Lin - Gentle and Friendly Narrator"},{id:"anika",voiceId:"ecp3DWciuUyW7BYM7II1",name:"Anika - Sweet and Lively"},{id:"bella",voiceId:"hpp4J3VqNfWAUOO0d1Us",name:"Bella - Professional, Bright, Warm"},{id:"bill",voiceId:"pqHfZKP75CvOlQylNhV4",name:"Bill - Wise, Mature, Balanced"},{id:"brian",voiceId:"nPczCjzI2devNBz1zQrb",name:"Brian - Deep, Resonant and Comforting"},{id:"callum",voiceId:"N2lVS1w4EtoT3dr4eOWO",name:"Callum - Husky Trickster"},{id:"charlie",voiceId:"IKne3meq5aSn9XLyUdCD",name:"Charlie - Deep, Confident, Energetic"},{id:"chris",voiceId:"iP95p4xoKVk53GoZ742B",name:"Chris - Charming, Down-to-Earth"},{id:"daniel",voiceId:"onwK4e9ZLuTAKqWW03F9",name:"Daniel - Steady Broadcaster"},{id:"eric",voiceId:"cjVigY5qzO86Huf0OWal",name:"Eric - Smooth, Trustworthy"},{id:"george",voiceId:"JBFqnCBsd6RMkjVDRZzb",name:"George - Warm, Captivating Storyteller"},{id:"harry",voiceId:"SOYHLrjzK2X1ezoPC6cr",name:"Harry - Fierce Warrior"},{id:"jessica",voiceId:"r1KmysJdVYZjJCm4mL3b",name:"Jessica - Playful, Bright, Warm"},{id:"keshavi",voiceId:"Ek86tj0PS0XTYchY9Ody",name:"Keshavi - Clear & Human AI Assistant"},{id:"laura",voiceId:"FGY2WhTYpPnrIDTdsKH5",name:"Laura - Enthusiast, Quirky Attitude"},{id:"liam",voiceId:"TX3LPaxmHKxFdv7VOQHJ",name:"Liam - Energetic, Social Media Creator"},{id:"lily",voiceId:"pFZP5JQG7iQjIQuC4Bku",name:"Lily - Velvety Actress"},{id:"matilda",voiceId:"XrExE9yKIg1WjnnlVkGX",name:"Matilda - Knowledgable, Professional"},{id:"river",voiceId:"SAz9YHcvj6GT2YYXdXww",name:"River - Relaxed, Neutral, Informative"},{id:"roger",voiceId:"CwhRBWXzGAHq8TQ4Fs17",name:"Roger - Laid-Back, Casual, Resonant"},{id:"sarah",voiceId:"EXAVITQu4vr4xnSDxMaL",name:"Sarah - Mature, Reassuring, Confident"},{id:"will",voiceId:"bIHbv24MWmeRgasZH58o",name:"Will - Relaxed Optimist"}];function ot(){return typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now()}-${Math.random().toString(16).slice(2)}`}function st(){if(typeof window>"u")return!1;try{return window.self!==window.top}catch{return!0}}function it(r,e,t,n){if(typeof window>"u")return Promise.reject(new Error("useVoices requires a browser environment"));let o=window.top??window.parent;return!o||o===window?Promise.reject(new Error("useVoices: aucun parent \u2014 utilisez la preview \xE9diteur ou une page int\xE9gr\xE9e.")):new Promise((s,i)=>{let u=ot(),a=d=>{let c=d.data;if(!(!c||c.type!=="sparkle-elevenlabs-speak-result"||c.requestId!==u))if(window.removeEventListener("message",a),window.clearTimeout(l),c.ok){let g=new Blob([c.buffer],{type:c.contentType}),f=URL.createObjectURL(g),p=new Audio(f);p.addEventListener("canplaythrough",()=>{p.play().catch(i),s(p)},{once:!0}),p.addEventListener("error",()=>{i(new Error("Failed to load audio"))}),p.load()}else i(new Error(typeof c.error=="string"?c.error:"elevenlabs_speak_failed"))},l=window.setTimeout(()=>{window.removeEventListener("message",a),i(new Error("useVoices: d\xE9lai d\xE9pass\xE9"))},45e3);window.addEventListener("message",a),o.postMessage({type:"sparkle-elevenlabs-speak",requestId:u,userId:r,token:e,text:t,options:n},"*")})}async function at(r,e,t,n){let o=new URLSearchParams;r&&o.set("userId",r),e&&o.set("k",e);let s=await fetch(`/api/obs/alerts/elevenlabs-tts?${o.toString()}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({text:t,...n})});if(!s.ok)throw new Error(`TTS Request failed with status ${s.status}`);let i=await s.blob(),u=URL.createObjectURL(i),a=new Audio(u);return new Promise((l,d)=>{a.addEventListener("canplaythrough",()=>{a.play().catch(d),l(a)},{once:!0}),a.addEventListener("error",()=>{d(new Error("Failed to load audio"))}),a.load()})}function vr(){let r=w(),e=r.userId?.trim()??"",t=r.apiKey?.trim()??"",n=st();return{speak:rt(async(s,i)=>{let u=nt.find(d=>d.id===i.voice),l={voiceId:u?u.voiceId:i.voice,voice_settings:i.voice_settings};return n?it(e,t,s,l):at(e,t,s,l)},[e,t,n])}}import{useCallback as ct,useState as z}from"react";function ut(){return typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now()}-${Math.random().toString(16).slice(2)}`}function lt(){if(typeof window>"u")return!1;try{return window.self!==window.top}catch{return!0}}function dt(r,e,t,n){if(typeof window>"u")return Promise.reject(new Error("useGenerate requires a browser environment"));let o=window.top??window.parent;return!o||o===window?Promise.reject(new Error("useGenerate: aucun parent \u2014 utilisez la preview \xE9diteur ou une page int\xE9gr\xE9e.")):new Promise((s,i)=>{let u=ut(),a=d=>{let c=d.data;!c||c.type!=="sparkle-ai-generate-result"||c.requestId!==u||(window.removeEventListener("message",a),window.clearTimeout(l),c.ok?s(c.text):i(new Error(typeof c.error=="string"?c.error:"ai_generate_failed")))},l=window.setTimeout(()=>{window.removeEventListener("message",a),i(new Error("useGenerate: d\xE9lai d\xE9pass\xE9"))},45e3);window.addEventListener("message",a),o.postMessage({type:"sparkle-ai-generate",requestId:u,userId:r,token:e,prompt:t,model:n},"*")})}async function pt(r,e,t,n){let o=new URLSearchParams;r&&o.set("userId",r),e&&o.set("k",e);let s=await fetch(`/api/obs/ai-generate?${o.toString()}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({prompt:t,model:n})});if(!s.ok){let u=await s.json().catch(()=>null);throw new Error(u?.error||`Generation failed with status ${s.status}`)}return(await s.json()).text}function Cr(){let r=w(),e=r.userId?.trim()??"",t=r.apiKey?.trim()??"",n=lt(),[o,s]=z(!1),[i,u]=z(null);return{generateText:ct(async(l,d)=>{s(!0),u(null);try{return n?await dt(e,t,l,d?.model):await pt(e,t,l,d?.model)}catch(c){let g=c instanceof Error?c:new Error("Unknown error generating text");throw u(g),g}finally{s(!1)}},[e,t,n]),isGenerating:o,error:i}}export{nt as ELEVENLABS_PRESET_VOICES,Yt as SparkleHookKeys,Gt as SparkleProvider,Wt as useAuth,Cr as useGenerate,Jt as useHook,fr as useLabels,_t as useRealtime,hr as useSpotify,er as useTask,ir as useTwitch,vr as useVoices};
1
+ import b,{useMemo as ue,useEffect as le,useState as de}from"react";import*as x from"react";import{createContext as H}from"react";var S=H({token:null}),L=({children:r,config:e})=>x.createElement(S.Provider,{value:{token:e.userId}},r);import Q,{useCallback as R,useContext as X,useRef as Z}from"react";import{createContext as ee,useEffect as B,useState as U}from"react";import $ from"events";import m from"zod";var K=m.object({type:m.literal("subscribe"),channels:m.array(m.string())}),Y=m.object({type:m.literal("hook"),channels:m.array(m.string())}),J=m.object({type:m.literal("unsubscribe"),channels:m.string()}),ht=m.object({event:m.literal("update"),type:m.enum(["value","hook"]),data:m.object({key:m.string(),value:m.string()})}),bt=m.union([K,J,Y]),y=class extends ${constructor(t){super();this.socket=null;this.queue=[];this.subscriptions=[];this.hooks=[];this.isAuthenticating=!1;this.jwt=null;this.url=t}send(t){this.socket?.readyState===WebSocket.OPEN?this.socket?.send(JSON.stringify(t)):this.queue.push(t)}authenticate(t){this.isAuthenticating||(this.isAuthenticating=!0,this.jwt=t,this.connect())}connect(){let t=this.url+"?jwt="+this.jwt;this.socket=new WebSocket(t),this.socket.onopen=()=>{this.queue.forEach(n=>this.send(n)),this.queue=[]},this.socket.onmessage=n=>{let o=JSON.parse(n.data);this.emit("update",o)},this.socket.onclose=()=>{this.reconnect()}}reconnect(){setTimeout(()=>{this.queue=[{type:"subscribe",channels:this.subscriptions},{type:"hook",channels:this.hooks}],console.log("try reconnecting"),this.connect()},5e3)}subscribe(t){let n=t.filter(o=>!this.subscriptions.includes(o));n.length&&(this.subscriptions=[...this.subscriptions,...n],console.log("subscribe",t),this.send({type:"subscribe",channels:t}))}hook(t){let n=[t].filter(o=>!this.hooks.includes(o));n.length&&(this.hooks=[...this.hooks,...n],this.send({type:"hook",channels:[t]}))}unsubscribe(t){this.send({type:"unsubscribe",channels:t})}disconnect(){this.socket&&(this.socket.onclose=()=>{},this.socket.close())}};var h=ee({subscribe:()=>{},listen:()=>{},values:{},hooks:{}}),M=({children:r,config:e})=>{let[t,n]=U({}),[o,s]=U({}),i=Z(null),{token:u}=X(S);B(()=>(i.current=new y(e.wsUrl),i.current.on("update",d=>{let{data:c,type:g}=d;if(g==="hook"){let f=JSON.parse(c.value);s(p=>({...p,[f.hook]:f.payload})),console.log("receive hooks",f)}else{let f=c.value;console.log("value",f);try{let p=parseFloat(c.value);if(!isNaN(p)&&String(p)===String(c.value).trim())f=p;else try{f=JSON.parse(c.value)}catch{f=c.value}}catch{f=c.value}n(p=>({...p,[c.key]:f})),console.log("receive values",t)}}),()=>{i.current?.disconnect()}),[]),B(()=>{!i.current||!u||i.current.authenticate(u)},[u]);let a=R(d=>{i.current?.subscribe(d)},[i]),l=R(d=>{i.current?.hook(d)},[i]);return Q.createElement(h.Provider,{value:{subscribe:a,listen:l,values:t,hooks:o}},r)};import te,{createContext as re,useCallback as ne,useEffect as oe,useRef as j,useState as se}from"react";var ie=re({status:"disconnected",call:()=>Promise.reject(new Error("ObsProvider not mounted"))});function O({url:r,children:e}){let[t,n]=se("disconnected"),o=j(null),s=j(!1);oe(()=>{if(!r)return;s.current=!1;let u;return(async()=>{let{default:l}=await import("obs-websocket-js"),d=new l;o.current=d;let c=async()=>{if(!s.current){n("connecting");try{await d.connect(r),s.current||n("connected")}catch{s.current||(n("error"),u=setTimeout(c,3e3))}}};d.on("ConnectionClosed",()=>{s.current||(n("disconnected"),u=setTimeout(c,3e3))}),d.on("ConnectionError",()=>{s.current||n("error")}),await c()})(),()=>{s.current=!0,clearTimeout(u),o.current?.disconnect(),o.current=null,n("disconnected")}},[r]);let i=ne(async(u,a)=>{let l=o.current;if(!l)throw new Error("OBS not connected");return l.call(u,a)},[]);return te.createElement(ie.Provider,{value:{status:t,call:i}},e)}import{createContext as ae,useContext as ce}from"react";var I=ae(null);function w(){let r=ce(I);if(!r)throw new Error("Sparkle hooks require SparkleProvider");return r}var pe=({children:r})=>{let[e,t]=de(!1);return le(()=>{t(!0)},[]),e?r:null},fe={userId:"",wsUrl:"wss://sparkle-bot-v2.fly.dev/ws"},Gt=({children:r,config:e})=>{let t={...fe,...e},n=ue(()=>t.obs?.clientId?`${t.obs.bridgeUrl??"wss://sparkle-bot-v2.fly.dev/obs-bridge"}?role=client&clientId=${encodeURIComponent(t.obs.clientId)}`:"",[t.obs?.clientId,t.obs?.bridgeUrl]),o=b.createElement(I.Provider,{value:t},b.createElement(L,{config:t},b.createElement(M,{config:t},b.createElement(pe,null,r))));return n?b.createElement(O,{url:n},o):o};import{useContext as me,useEffect as ge,useMemo as we}from"react";function _t(r,e){let t=me(h),n=we(()=>[r],[r]);if(!t)throw new Error("You must use useRealtime inside a SparkleProvider");return ge(()=>{t.subscribe([r])},[n]),n.map(o=>t.values[o]??e)}import{useEffect as he,useState as E}from"react";var be="sparkle-widget-session-request",ye="sparkle-widget-session-result";function ke(){return typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now()}-${Math.random().toString(16).slice(2)}`}function ve(){if(typeof window>"u")return!1;try{return window.self!==window.top}catch{return!0}}function G(r){if(!r||typeof r!="object")return{userId:null,username:null,profilePicture:null};let e=r,t=typeof e.userId=="string"&&e.userId.trim()?e.userId:null,n=typeof e.username=="string"&&e.username.trim()?e.username:null,o=typeof e.profilePicture=="string"&&e.profilePicture.trim()?e.profilePicture:null;return{userId:t,username:n,profilePicture:o}}function Se(){if(typeof window>"u")return Promise.resolve({userId:null,username:null,profilePicture:null});let r=window.top??window.parent;return!r||r===window?Promise.resolve({userId:null,username:null,profilePicture:null}):new Promise(e=>{let t=ke(),n=s=>{let i=s.data;!i||i.type!==ye||i.requestId!==t||(window.removeEventListener("message",n),window.clearTimeout(o),e(G(i)))},o=window.setTimeout(()=>{window.removeEventListener("message",n),e({userId:null,username:null,profilePicture:null})},12e3);window.addEventListener("message",n),r.postMessage({type:be,requestId:t},"*")})}async function Ie(){let r=await fetch("/api/sparkle-widget-session",{method:"GET",credentials:"include"});if(!r.ok)return{userId:null,username:null,profilePicture:null};let e=await r.json().catch(()=>null);return G(e)}function Wt(){let[r,e]=E(void 0),[t,n]=E(void 0),[o,s]=E(void 0);return he(()=>{let i=!1;return(async()=>{let a=ve()?await Se():await Ie();i||(e(a.userId),n(a.username),s(a.profilePicture))})(),()=>{i=!0}},[]),{userId:r,username:t,profilePicture:o}}import{useContext as Ee,useEffect as Ce}from"react";var Yt=["notification.follow","notification.cheer","notification.tips","notification.subscribe","notification.subscribe.end","notification.subscribe.gift","notification.subscription.message","notification.raid","chat.message","stream.online","stream.offline","stream.update","reward.create","reward.update","reward.remove","reward.redemption.claim","reward.redemption.update","poll.begin","poll.progress","poll.end","prediction.begin","prediction.progress","prediction.lock","prediction.end","moderator.add","moderator.remove","moderator.ban","moderator.unban","moderator.shield_mode.begin"];function Jt(r){let e=Ee(h);if(!e)throw new Error("You must use useHook inside a SparkleProvider");return Ce(()=>{e.listen(r)},[r]),e.hooks[r]}import{useCallback as q,useMemo as Pe}from"react";var Te="sparkle-invoke-task",xe="sparkle-invoke-task-result";function Le(){return typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now()}-${Math.random().toString(16).slice(2)}`}function A(r,e){if(typeof window>"u")return Promise.reject(new Error("useTask requires a browser environment"));let t=window.top??window.parent;return!t||t===window?Promise.reject(new Error("useTask: aucun parent \u2014 utilisez la preview \xE9diteur ou une page int\xE9gr\xE9e.")):new Promise((n,o)=>{let s=Le(),i=a=>{let l=a.data;!l||l.type!==xe||l.requestId!==s||(window.removeEventListener("message",i),window.clearTimeout(u),l.ok?n(!!l.result):o(new Error(typeof l.error=="string"?l.error:"invoke_failed")))},u=window.setTimeout(()=>{window.removeEventListener("message",i),o(new Error("useTask: d\xE9lai d\xE9pass\xE9"))},45e3);window.addEventListener("message",i),t.postMessage({type:Te,requestId:s,projectId:r,...e},"*")})}function D(){if(typeof window>"u")return!1;try{return window.self!==window.top}catch{return!0}}async function N(r,e){let t=await fetch(`/api/projects/${r}/invoke-task`,{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!t.ok){let n=await t.json().catch(()=>null);throw new Error(n?.error??t.statusText)}return!0}function er(){let e=w().projectId,t=q(async(o,s)=>{if(!e?.trim())throw new Error("useTask: renseignez `config.projectId` sur SparkleProvider.");let i={kind:"invoke",taskId:o,payload:s};return D()?A(e,i):N(e,i)},[e]),n=q(async(o,s,i)=>{if(!e?.trim())throw new Error("useTask: renseignez `config.projectId` sur SparkleProvider.");let u={kind:"invokeUntil",taskId:o,payload:i,delaySeconds:s};return D()?A(e,u):N(e,u)},[e]);return Pe(()=>({invoke:t,invokeUntil:n}),[t,n])}import{useCallback as Re,useMemo as Be}from"react";function _(r){let e=(t,n)=>(...o)=>r(t,n,o);return{bits:{getLeaderboard:e("bits","getLeaderboard")},channelPoints:{getCustomRewards:e("channelPoints","getCustomRewards"),getCustomRewardsByIds:e("channelPoints","getCustomRewardsByIds"),getCustomRewardById:e("channelPoints","getCustomRewardById"),getRedemptionsByIds:e("channelPoints","getRedemptionsByIds"),getRedemptionById:e("channelPoints","getRedemptionById"),getRedemptions:e("channelPoints","getRedemptions")},channel:{getChannelInfoById:e("channel","getChannelInfoById"),getFollowDate:e("channel","getFollowDate"),getChannelEditors:e("channel","getChannelEditors"),getVips:e("channel","getVips"),checkVipForUsers:e("channel","checkVipForUsers"),checkVipForUser:e("channel","checkVipForUser"),getChannelFollowerCount:e("channel","getChannelFollowerCount")},chat:{getChatters:e("chat","getChatters"),getSettings:e("chat","getSettings"),getSettingsPrivileged:e("chat","getSettingsPrivileged"),getColorsForUsers:e("chat","getColorsForUsers"),getColorForUser:e("chat","getColorForUser")},clips:{getClips:e("clips","getClips"),getClipById:e("clips","getClipById"),getClipsByIds:e("clips","getClipsByIds")},goals:{getGoals:e("goals","getGoals")},polls:{getPolls:e("polls","getPolls"),getPollsByIds:e("polls","getPollsByIds"),getPollById:e("polls","getPollById")},predictions:{getPredictions:e("predictions","getPredictions"),getPredictionsByIds:e("predictions","getPredictionsByIds"),getPredictionById:e("predictions","getPredictionById")},streams:{getStream:e("streams","getStream"),getStreamMarkers:e("streams","getStreamMarkers")},subscriptions:{getSubscriptions:e("subscriptions","getSubscriptions"),getSubscriptionsForUsers:e("subscriptions","getSubscriptionsForUsers"),getSubscriptionForUser:e("subscriptions","getSubscriptionForUser"),checkUserSubscription:e("subscriptions","checkUserSubscription")},moderation:{getBanned:e("moderation","getBanned"),checkBan:e("moderation","checkBan"),getModerators:e("moderation","getModerators"),checkModerator:e("moderation","checkModerator"),getBlockedTerms:e("moderation","getBlockedTerms")},utils:{getUserById:e("utils","getUserById"),getUserByName:e("utils","getUserByName"),getUserByIds:e("utils","getUserByIds"),getUserByNames:e("utils","getUserByNames")}}}var Ue="sparkle-twitch-get",Me="sparkle-twitch-get-result";function je(){return typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now()}-${Math.random().toString(16).slice(2)}`}function Oe(){if(typeof window>"u")return!1;try{return window.self!==window.top}catch{return!0}}function Ge(r,e){if(typeof window>"u")return Promise.reject(new Error("useTwitch requires a browser environment"));let t=window.top??window.parent;return!t||t===window?Promise.reject(new Error("useTwitch: aucun parent \u2014 utilisez la preview \xE9diteur ou une page int\xE9gr\xE9e.")):new Promise((n,o)=>{let s=je(),i=a=>{let l=a.data;!l||l.type!==Me||l.requestId!==s||(window.removeEventListener("message",i),window.clearTimeout(u),l.ok?n(l.result):o(new Error(typeof l.error=="string"?l.error:"twitch_get_failed")))},u=window.setTimeout(()=>{window.removeEventListener("message",i),o(new Error("useTwitch: d\xE9lai d\xE9pass\xE9"))},45e3);window.addEventListener("message",i),t.postMessage({type:Ue,requestId:s,projectId:r,...e},"*")})}async function qe(r,e){let t=await fetch(`/api/projects/${encodeURIComponent(r)}/twitch-get`,{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!t.ok){let o=await t.json().catch(()=>null);throw new Error(o?.error??t.statusText)}return(await t.json()).result}function ir(){let e=w().projectId,t=Re(async(n,o,s)=>{if(!e?.trim())throw new Error("useTwitch: renseignez `config.projectId` sur SparkleProvider.");let i={path:[n,o],args:s};return Oe()?Ge(e,i):qe(e,i)},[e]);return Be(()=>_(t),[t])}import{useCallback as De,useContext as Ne,useEffect as P,useRef as _e,useState as Fe}from"react";function F(r){if(!r||typeof r!="object")return null;let e=r,t=n=>{let o=Number(e[n]);return Number.isFinite(o)?Math.max(0,Math.floor(o)):0};return{followers:t("followers"),subscribers:t("subscribers"),cheers:t("cheers"),tips:t("tips")}}function Ae(r){if(!r||typeof r!="object")return null;let e=r,t=e.cheers&&typeof e.cheers=="object"?e.cheers:null,n=e.tips&&typeof e.tips=="object"?e.tips:null;return{followers:typeof e.followers=="string"?e.followers:"",subscribers:typeof e.subscribers=="string"?e.subscribers:"",cheers:{amount:t&&Number.isFinite(Number(t.amount))?Math.max(0,Math.floor(Number(t.amount))):0,user:t&&typeof t.user=="string"?t.user:""},tips:{amount:n&&Number.isFinite(Number(n.amount))?Number(n.amount):0,user:n&&typeof n.user=="string"?n.user:""}}}function k(r){return Array.isArray(r)?r.filter(e=>typeof e=="string"):[]}function C(r){if(!r||typeof r!="object")return null;let e=r,t=F(e.session),n=F(e.total),o=Ae(e.latest),s=e.recent&&typeof e.recent=="object"?e.recent:null;return!t||!n||!o||!s?null:{session:t,total:n,latest:o,recent:{followers:k(s.followers),subscribers:k(s.subscribers),cheers:k(s.cheers),tips:k(s.tips)}}}var v=()=>({session:{followers:0,subscribers:0,cheers:0,tips:0},total:{followers:0,subscribers:0,cheers:0,tips:0},latest:{followers:"",subscribers:"",cheers:{amount:0,user:""},tips:{amount:0,user:""}},recent:{followers:[],subscribers:[],cheers:[],tips:[]}});var V="labels:snapshot",Ve="sparkle-labels-get",We="sparkle-labels-get-result";function ze(){return typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now()}-${Math.random().toString(16).slice(2)}`}function He(){if(typeof window>"u")return!1;try{return window.self!==window.top}catch{return!0}}function $e(r){if(typeof window>"u")return Promise.reject(new Error("useLabels requires a browser environment"));let e=window.top??window.parent;return!e||e===window?Promise.reject(new Error("useLabels: aucun parent \u2014 utilisez la preview \xE9diteur ou une page int\xE9gr\xE9e.")):new Promise((t,n)=>{let o=ze(),s=u=>{let a=u.data;!a||a.type!==We||a.requestId!==o||(window.removeEventListener("message",s),window.clearTimeout(i),a.ok&&a.labels?t(a.labels):n(new Error(typeof a.error=="string"?a.error:"labels_get_failed")))},i=window.setTimeout(()=>{window.removeEventListener("message",s),n(new Error("useLabels: d\xE9lai d\xE9pass\xE9"))},45e3);window.addEventListener("message",s),e.postMessage({type:Ve,requestId:o,projectId:r},"*")})}async function Ke(r){let e=await fetch(`/api/projects/${encodeURIComponent(r)}/labels`,{method:"GET",credentials:"include"});if(!e.ok){let n=await e.json().catch(()=>null);throw new Error(n?.error??e.statusText)}return(await e.json()).labels??v()}async function Ye(r,e){return e?$e(r):Ke(r)}function fr(){let r=w(),e=r.projectId?.trim()??"",t=r.userId?.trim()??"",n=Ne(h),o=!!t,s=He()&&!o,[i,u]=Fe(v),a=_e(0),l=n.values[V];P(()=>{o&&n.subscribe([V])},[o,n]),P(()=>{if(!o)return;let c=C(l);c&&u(c)},[o,l]);let d=De(async()=>{if(!e)return;let c=++a.current;try{let g=await Ye(e,s);c===a.current&&u(g)}catch{}},[e,s]);return P(()=>{if(!e){u(v());return}if(o){C(l)||d();return}d()},[e,d,o,l]),i}import{useCallback as Je,useEffect as W,useRef as Qe,useState as T}from"react";function Xe(){return typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now()}-${Math.random().toString(16).slice(2)}`}function Ze(){if(typeof window>"u")return!1;try{return window.self!==window.top}catch{return!0}}function et(r){if(typeof window>"u")return Promise.reject(new Error("useSpotify requires a browser environment"));let e=window.top??window.parent;return!e||e===window?Promise.reject(new Error("useSpotify: aucun parent \u2014 utilisez la preview \xE9diteur ou une page int\xE9gr\xE9e.")):new Promise((t,n)=>{let o=Xe(),s=u=>{let a=u.data;!a||a.type!=="sparkle-spotify-get-result"||a.requestId!==o||(window.removeEventListener("message",s),window.clearTimeout(i),a.ok?t(a.track):n(new Error(typeof a.error=="string"?a.error:"spotify_get_failed")))},i=window.setTimeout(()=>{window.removeEventListener("message",s),n(new Error("useSpotify: d\xE9lai d\xE9pass\xE9"))},45e3);window.addEventListener("message",s),e.postMessage({type:"sparkle-spotify-get",requestId:o,projectId:r},"*")})}async function tt(r){let e=await fetch(`/api/projects/${encodeURIComponent(r)}/spotify`,{method:"GET",credentials:"include"});if(!e.ok){let n=await e.json().catch(()=>null);throw new Error(n?.error??e.statusText)}return(await e.json()).track??null}function hr(r){let t=w().projectId?.trim()??"",n=r?.intervalMs??5e3,o=Ze(),[s,i]=T(null),[u,a]=T(!0),[l,d]=T(null),c=Qe(0),g=Je(async()=>{if(!t){i(null),a(!1);return}let f=++c.current;try{let p=o?await et(t):await tt(t);f===c.current&&(i(p),d(null))}catch(p){f===c.current&&d(p instanceof Error?p:new Error(String(p)))}finally{f===c.current&&a(!1)}},[t,o]);return W(()=>{g()},[g]),W(()=>{if(n<=0||!t)return;let f=setInterval(()=>{g()},n);return()=>clearInterval(f)},[g,n,t]),{track:s,loading:u,error:l,refresh:g}}import{useCallback as rt}from"react";var nt=[{id:"adam",voiceId:"pNInz6obpgDQGcFmaJgB",name:"Adam - Dominant, Firm"},{id:"alice",voiceId:"Xb7hH8MSUJpSbSDYk0k2",name:"Alice - Clear, Engaging Educator"},{id:"andy-lin",voiceId:"h1i3CVVBUuF6s46cxUGG",name:"Andy Lin - Gentle and Friendly Narrator"},{id:"anika",voiceId:"ecp3DWciuUyW7BYM7II1",name:"Anika - Sweet and Lively"},{id:"bella",voiceId:"hpp4J3VqNfWAUOO0d1Us",name:"Bella - Professional, Bright, Warm"},{id:"bill",voiceId:"pqHfZKP75CvOlQylNhV4",name:"Bill - Wise, Mature, Balanced"},{id:"brian",voiceId:"nPczCjzI2devNBz1zQrb",name:"Brian - Deep, Resonant and Comforting"},{id:"callum",voiceId:"N2lVS1w4EtoT3dr4eOWO",name:"Callum - Husky Trickster"},{id:"charlie",voiceId:"IKne3meq5aSn9XLyUdCD",name:"Charlie - Deep, Confident, Energetic"},{id:"chris",voiceId:"iP95p4xoKVk53GoZ742B",name:"Chris - Charming, Down-to-Earth"},{id:"daniel",voiceId:"onwK4e9ZLuTAKqWW03F9",name:"Daniel - Steady Broadcaster"},{id:"eric",voiceId:"cjVigY5qzO86Huf0OWal",name:"Eric - Smooth, Trustworthy"},{id:"george",voiceId:"JBFqnCBsd6RMkjVDRZzb",name:"George - Warm, Captivating Storyteller"},{id:"harry",voiceId:"SOYHLrjzK2X1ezoPC6cr",name:"Harry - Fierce Warrior"},{id:"jessica",voiceId:"r1KmysJdVYZjJCm4mL3b",name:"Jessica - Playful, Bright, Warm"},{id:"keshavi",voiceId:"Ek86tj0PS0XTYchY9Ody",name:"Keshavi - Clear & Human AI Assistant"},{id:"laura",voiceId:"FGY2WhTYpPnrIDTdsKH5",name:"Laura - Enthusiast, Quirky Attitude"},{id:"liam",voiceId:"TX3LPaxmHKxFdv7VOQHJ",name:"Liam - Energetic, Social Media Creator"},{id:"lily",voiceId:"pFZP5JQG7iQjIQuC4Bku",name:"Lily - Velvety Actress"},{id:"matilda",voiceId:"XrExE9yKIg1WjnnlVkGX",name:"Matilda - Knowledgable, Professional"},{id:"river",voiceId:"SAz9YHcvj6GT2YYXdXww",name:"River - Relaxed, Neutral, Informative"},{id:"roger",voiceId:"CwhRBWXzGAHq8TQ4Fs17",name:"Roger - Laid-Back, Casual, Resonant"},{id:"sarah",voiceId:"EXAVITQu4vr4xnSDxMaL",name:"Sarah - Mature, Reassuring, Confident"},{id:"will",voiceId:"bIHbv24MWmeRgasZH58o",name:"Will - Relaxed Optimist"}];function ot(){return typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now()}-${Math.random().toString(16).slice(2)}`}function st(){if(typeof window>"u")return!1;try{return window.self!==window.top}catch{return!0}}function it(r,e,t,n){if(typeof window>"u")return Promise.reject(new Error("useVoices requires a browser environment"));let o=window.top??window.parent;return!o||o===window?Promise.reject(new Error("useVoices: aucun parent \u2014 utilisez la preview \xE9diteur ou une page int\xE9gr\xE9e.")):new Promise((s,i)=>{let u=ot(),a=d=>{let c=d.data;if(!(!c||c.type!=="sparkle-elevenlabs-speak-result"||c.requestId!==u))if(window.removeEventListener("message",a),window.clearTimeout(l),c.ok){let g=new Blob([c.buffer],{type:c.contentType}),f=URL.createObjectURL(g),p=new Audio(f);p.addEventListener("canplaythrough",()=>{p.play().catch(i),s(p)},{once:!0}),p.addEventListener("error",()=>{i(new Error("Failed to load audio"))}),p.load()}else i(new Error(typeof c.error=="string"?c.error:"elevenlabs_speak_failed"))},l=window.setTimeout(()=>{window.removeEventListener("message",a),i(new Error("useVoices: d\xE9lai d\xE9pass\xE9"))},45e3);window.addEventListener("message",a),o.postMessage({type:"sparkle-elevenlabs-speak",requestId:u,userId:r,token:e,text:t,options:n},"*")})}async function at(r,e,t,n){let o=new URLSearchParams;r&&o.set("userId",r),e&&o.set("k",e);let s=await fetch(`/api/obs/alerts/elevenlabs-tts?${o.toString()}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({text:t,...n})});if(!s.ok)throw new Error(`TTS Request failed with status ${s.status}`);let i=await s.blob(),u=URL.createObjectURL(i),a=new Audio(u);return new Promise((l,d)=>{a.addEventListener("canplaythrough",()=>{a.play().catch(d),l(a)},{once:!0}),a.addEventListener("error",()=>{d(new Error("Failed to load audio"))}),a.load()})}function vr(){let r=w(),e=r.userId?.trim()??"",t=r.apiKey?.trim()??"",n=st();return{speak:rt(async(s,i)=>{let u=nt.find(d=>d.id===i.voice),l={voiceId:u?u.voiceId:i.voice,voice_settings:i.voice_settings};return n?it(e,t,s,l):at(e,t,s,l)},[e,t,n])}}import{useCallback as ct,useState as z}from"react";function ut(){return typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now()}-${Math.random().toString(16).slice(2)}`}function lt(){if(typeof window>"u")return!1;try{return window.self!==window.top}catch{return!0}}function dt(r,e,t,n){if(typeof window>"u")return Promise.reject(new Error("useGenerate requires a browser environment"));let o=window.top??window.parent;return!o||o===window?Promise.reject(new Error("useGenerate: aucun parent \u2014 utilisez la preview \xE9diteur ou une page int\xE9gr\xE9e.")):new Promise((s,i)=>{let u=ut(),a=d=>{let c=d.data;!c||c.type!=="sparkle-ai-generate-result"||c.requestId!==u||(window.removeEventListener("message",a),window.clearTimeout(l),c.ok?s(c.text):i(new Error(typeof c.error=="string"?c.error:"ai_generate_failed")))},l=window.setTimeout(()=>{window.removeEventListener("message",a),i(new Error("useGenerate: d\xE9lai d\xE9pass\xE9"))},45e3);window.addEventListener("message",a),o.postMessage({type:"sparkle-ai-generate",requestId:u,userId:r,token:e,prompt:t,model:n},"*")})}async function pt(r,e,t,n){let o=new URLSearchParams;r&&o.set("userId",r),e&&o.set("k",e);let s=await fetch(`/api/obs/ai-generate?${o.toString()}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({prompt:t,model:n})});if(!s.ok){let u=await s.json().catch(()=>null);throw new Error(u?.error||`Generation failed with status ${s.status}`)}return(await s.json()).text}function Cr(){let r=w(),e=r.userId?.trim()??"",t=r.apiKey?.trim()??"",n=lt(),[o,s]=z(!1),[i,u]=z(null);return{generateText:ct(async(l,d)=>{s(!0),u(null);try{return n?await dt(e,t,l,d?.model):await pt(e,t,l,d?.model)}catch(c){let g=c instanceof Error?c:new Error("Unknown error generating text");throw u(g),g}finally{s(!1)}},[e,t,n]),isGenerating:o,error:i}}export{nt as ELEVENLABS_PRESET_VOICES,Yt as SparkleHookKeys,Gt as SparkleProvider,Wt as useAuth,Cr as useGenerate,Jt as useHook,fr as useLabels,_t as useRealtime,hr as useSpotify,er as useTask,ir as useTwitch,vr as useVoices};
2
2
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/provider.tsx","../src/providers/authProvider.tsx","../src/providers/realtimeProvider.tsx","../src/lib/websocket.ts","../src/providers/obsProvider.tsx","../src/sparkle-config-context.tsx","../src/hooks/useRealtime.ts","../src/hooks/useAuth.ts","../src/hooks/useHook.ts","../src/hooks/useTask.ts","../src/hooks/useTwitch.ts","../src/twitch/create-twitch-get-client.ts","../src/hooks/useLabels.ts","../src/types/labels.ts","../src/hooks/useSpotify.ts","../src/hooks/useVoices.ts","../src/hooks/useGenerate.ts"],"sourcesContent":["import React, { useMemo, type ReactNode, useEffect, useState } from \"react\"\nimport { AuthProvider } from \"./providers/authProvider\"\nimport { WebSocketProvider } from \"./providers/realtimeProvider\"\nimport { ObsProvider } from \"./providers/obsProvider\"\nimport { SparkleConfigContext } from \"./sparkle-config-context\"\nimport { SparkleConfig } from \"./utils/config\"\n\nconst ClientGate = ({ children }: { children: ReactNode }) => {\n const [ready, setReady] = useState(false)\n\n useEffect(() => {\n setReady(true)\n }, [])\n\n if (!ready) return null\n return children\n}\n\nexport interface SparkleProviderProps {\n config?: SparkleConfig\n children: ReactNode\n}\n\nconst defaultConfig: SparkleConfig = {\n userId: \"\",\n wsUrl: \"wss://sparkle-bot-v2.fly.dev/ws\",\n}\n\nexport const SparkleProvider = ({ children, config }: SparkleProviderProps) => {\n const mergedConfig = { ...defaultConfig, ...config }\n\n const obsUrl = useMemo(() => {\n if (!mergedConfig.obs?.clientId) return \"\"\n const base =\n mergedConfig.obs.bridgeUrl ?? \"wss://sparkle-bot-v2.fly.dev/obs-bridge\"\n return `${base}?role=client&clientId=${encodeURIComponent(mergedConfig.obs.clientId)}`\n }, [mergedConfig.obs?.clientId, mergedConfig.obs?.bridgeUrl])\n\n const inner = (\n <SparkleConfigContext.Provider value={mergedConfig}>\n <AuthProvider config={mergedConfig}>\n <WebSocketProvider config={mergedConfig}>\n <ClientGate>{children}</ClientGate>\n </WebSocketProvider>\n </AuthProvider>\n </SparkleConfigContext.Provider>\n )\n\n if (obsUrl) {\n return <ObsProvider url={obsUrl}>{inner}</ObsProvider>\n }\n\n return inner\n}\n","import * as React from \"react\"\r\n\r\nimport { type ReactNode, createContext } from \"react\"\r\n\r\nimport { SparkleConfig } from \"../utils/config\"\r\n\r\ninterface AuthContextType {\r\n token: string | undefined | null\r\n}\r\n\r\nexport const SparkleAuthContext = createContext<AuthContextType>({\r\n token: null,\r\n})\r\n\r\ninterface AuthProviderProps {\r\n config: SparkleConfig\r\n children: ReactNode\r\n}\r\n\r\nexport const AuthProvider = ({ children, config }: AuthProviderProps) => {\r\n return (\r\n <SparkleAuthContext.Provider\r\n value={{\r\n token: config.userId,\r\n }}\r\n >\r\n {children}\r\n </SparkleAuthContext.Provider>\r\n )\r\n}\r\n","import React, { ReactNode, useCallback, useContext, useRef } from \"react\"\r\n\r\nimport { createContext, useEffect, useState } from \"react\"\r\nimport { SparkleAuthContext } from \"./authProvider\"\r\nimport { UpdateMessage, WebSocketClient } from \"../lib/websocket\"\r\nimport { SparkleConfig } from \"../utils/config\"\r\n\r\ninterface WebSocketContextType {\r\n subscribe: (keys: string[]) => void\r\n listen: (hook: string) => void\r\n values: Record<string, any>\r\n hooks: Record<string, any>\r\n}\r\n\r\nexport const RealtimeContext = createContext<WebSocketContextType>({\r\n subscribe: () => {},\r\n listen: () => {},\r\n values: {},\r\n hooks: {},\r\n})\r\n\r\nexport type RealtimeValue = string | number | boolean | { [key: string]: any }\r\n\r\ntype State = Record<string, RealtimeValue>\r\n\r\ninterface WebSocketProviderProps {\r\n config: SparkleConfig\r\n children: ReactNode\r\n}\r\n\r\nexport const WebSocketProvider = ({\r\n children,\r\n config,\r\n}: WebSocketProviderProps) => {\r\n const [values, setValues] = useState<State>({})\r\n const [hooks, setHooks] = useState<Record<string, any>>({})\r\n\r\n const socket = useRef<WebSocketClient>(null)\r\n\r\n const { token } = useContext(SparkleAuthContext)\r\n\r\n useEffect(() => {\r\n socket.current = new WebSocketClient(config.wsUrl)\r\n\r\n socket.current.on(\"update\", (message: UpdateMessage) => {\r\n const { data, type } = message\r\n\r\n if (type === \"hook\") {\r\n const payload = JSON.parse(data.value)\r\n\r\n setHooks((prev) => ({\r\n ...prev,\r\n [payload.hook]: payload.payload,\r\n }))\r\n console.log(\"receive hooks\", payload)\r\n } else {\r\n let value: any = data.value\r\n\r\n console.log(\"value\", value)\r\n\r\n try {\r\n const num = parseFloat(data.value)\r\n if (!isNaN(num) && String(num) === String(data.value).trim()) {\r\n value = num\r\n } else {\r\n try {\r\n value = JSON.parse(data.value)\r\n } catch {\r\n value = data.value\r\n }\r\n }\r\n } catch {\r\n value = data.value\r\n }\r\n\r\n setValues((prev) => ({ ...prev, [data.key]: value }))\r\n console.log(\"receive values\", values)\r\n }\r\n })\r\n\r\n return () => {\r\n socket.current?.disconnect()\r\n }\r\n }, [])\r\n\r\n useEffect(() => {\r\n if (!socket.current || !token) return\r\n\r\n socket.current.authenticate(token)\r\n }, [token])\r\n\r\n const subscribe = useCallback(\r\n (keys: string[]) => {\r\n socket.current?.subscribe(keys)\r\n },\r\n [socket]\r\n )\r\n\r\n const listen = useCallback(\r\n (hook: string) => {\r\n socket.current?.hook(hook)\r\n },\r\n [socket]\r\n )\r\n\r\n return (\r\n <RealtimeContext.Provider\r\n value={{\r\n subscribe,\r\n listen,\r\n values,\r\n hooks,\r\n }}\r\n >\r\n {children}\r\n </RealtimeContext.Provider>\r\n )\r\n}\r\n","import EventEmitter from \"events\"\r\nimport z from \"zod\"\r\n\r\nconst subscribeSchema = z.object({\r\n type: z.literal(\"subscribe\"),\r\n channels: z.array(z.string()),\r\n})\r\n\r\nconst hookSchema = z.object({\r\n type: z.literal(\"hook\"),\r\n channels: z.array(z.string()),\r\n})\r\n\r\nconst unsubscribeSchema = z.object({\r\n type: z.literal(\"unsubscribe\"),\r\n channels: z.string(),\r\n})\r\n\r\nconst updateSchema = z.object({\r\n event: z.literal(\"update\"),\r\n type: z.enum([\"value\", \"hook\"]),\r\n data: z.object({\r\n key: z.string(),\r\n value: z.string(),\r\n }),\r\n})\r\n\r\nconst messageSchema = z.union([subscribeSchema, unsubscribeSchema, hookSchema])\r\n\r\nexport type Message = z.infer<typeof messageSchema>\r\n\r\nexport type UpdateMessage = z.infer<typeof updateSchema>\r\nexport type SubscribeMessage = z.infer<typeof subscribeSchema>\r\nexport type UnsubscribeMessage = z.infer<typeof unsubscribeSchema>\r\n\r\nexport class WebSocketClient extends EventEmitter {\r\n private socket: WebSocket | null = null\r\n private queue: Message[] = []\r\n private subscriptions: string[] = []\r\n private hooks: string[] = []\r\n private isAuthenticating = false\r\n\r\n private url: string\r\n private jwt: string | null = null\r\n\r\n constructor(url: string) {\r\n super()\r\n this.url = url\r\n }\r\n\r\n private send(data: Message) {\r\n if (this.socket?.readyState === WebSocket.OPEN) {\r\n this.socket?.send(JSON.stringify(data))\r\n } else {\r\n this.queue.push(data)\r\n }\r\n }\r\n\r\n authenticate(jwt: string) {\r\n if (this.isAuthenticating) return\r\n this.isAuthenticating = true\r\n this.jwt = jwt\r\n this.connect()\r\n }\r\n\r\n private connect() {\r\n const authenticatedUrl = this.url + \"?jwt=\" + this.jwt\r\n this.socket = new WebSocket(authenticatedUrl)\r\n\r\n this.socket.onopen = () => {\r\n this.queue.forEach((data) => this.send(data))\r\n this.queue = []\r\n }\r\n\r\n this.socket.onmessage = (event) => {\r\n const data = JSON.parse(event.data)\r\n this.emit(\"update\", data)\r\n }\r\n\r\n this.socket.onclose = () => {\r\n this.reconnect()\r\n }\r\n }\r\n\r\n reconnect() {\r\n setTimeout(() => {\r\n this.queue = [\r\n {\r\n type: \"subscribe\",\r\n channels: this.subscriptions,\r\n },\r\n {\r\n type: \"hook\",\r\n channels: this.hooks,\r\n },\r\n ]\r\n\r\n console.log(\"try reconnecting\")\r\n\r\n this.connect()\r\n }, 5000)\r\n }\r\n\r\n subscribe(channels: string[]) {\r\n const newChannels = channels.filter(\r\n (channel) => !this.subscriptions.includes(channel),\r\n )\r\n\r\n if (!newChannels.length) return\r\n this.subscriptions = [...this.subscriptions, ...newChannels]\r\n\r\n console.log(\"subscribe\", channels)\r\n\r\n this.send({\r\n type: \"subscribe\",\r\n channels,\r\n })\r\n }\r\n\r\n hook(hook: string) {\r\n const newChannels = [hook].filter(\r\n (channel) => !this.hooks.includes(channel),\r\n )\r\n\r\n if (!newChannels.length) return\r\n this.hooks = [...this.hooks, ...newChannels]\r\n\r\n this.send({\r\n type: \"hook\",\r\n channels: [hook],\r\n })\r\n }\r\n\r\n unsubscribe(channels: string) {\r\n this.send({ type: \"unsubscribe\", channels })\r\n }\r\n\r\n disconnect() {\r\n if (!this.socket) return\r\n\r\n this.socket.onclose = () => {}\r\n this.socket.close()\r\n }\r\n}\r\n","import React, {\r\n createContext,\r\n useCallback,\r\n useEffect,\r\n useRef,\r\n useState,\r\n type ReactNode,\r\n} from \"react\"\r\nimport type OBSWebSocket from \"obs-websocket-js\"\r\nimport type { OBSRequestTypes, OBSResponseTypes } from \"obs-websocket-js\"\r\n\r\ntype ObsStatus = \"disconnected\" | \"connecting\" | \"connected\" | \"error\"\r\n\r\ninterface ObsContextType {\r\n status: ObsStatus\r\n call: <T extends keyof OBSRequestTypes>(\r\n requestType: T,\r\n requestData?: OBSRequestTypes[T]\r\n ) => Promise<OBSResponseTypes[T]>\r\n}\r\n\r\nexport const ObsContext = createContext<ObsContextType>({\r\n status: \"disconnected\",\r\n call: () => Promise.reject(new Error(\"ObsProvider not mounted\")),\r\n})\r\n\r\nexport interface ObsProviderProps {\r\n url: string\r\n children: ReactNode\r\n}\r\n\r\nexport function ObsProvider({ url, children }: ObsProviderProps) {\r\n const [status, setStatus] = useState<ObsStatus>(\"disconnected\")\r\n const obsRef = useRef<OBSWebSocket | null>(null)\r\n const cancelledRef = useRef(false)\r\n\r\n useEffect(() => {\r\n if (!url) return\r\n\r\n cancelledRef.current = false\r\n let reconnectTimer: ReturnType<typeof setTimeout>\r\n\r\n const setup = async () => {\r\n const { default: OBSWebSocket } = await import(\"obs-websocket-js\")\r\n const obs = new OBSWebSocket()\r\n obsRef.current = obs\r\n\r\n const doConnect = async () => {\r\n if (cancelledRef.current) return\r\n setStatus(\"connecting\")\r\n try {\r\n await obs.connect(url)\r\n if (!cancelledRef.current) setStatus(\"connected\")\r\n } catch {\r\n if (!cancelledRef.current) {\r\n setStatus(\"error\")\r\n reconnectTimer = setTimeout(doConnect, 3000)\r\n }\r\n }\r\n }\r\n\r\n obs.on(\"ConnectionClosed\" as any, () => {\r\n if (!cancelledRef.current) {\r\n setStatus(\"disconnected\")\r\n reconnectTimer = setTimeout(doConnect, 3000)\r\n }\r\n })\r\n\r\n obs.on(\"ConnectionError\" as any, () => {\r\n if (!cancelledRef.current) setStatus(\"error\")\r\n })\r\n\r\n await doConnect()\r\n }\r\n\r\n setup()\r\n\r\n return () => {\r\n cancelledRef.current = true\r\n clearTimeout(reconnectTimer!)\r\n obsRef.current?.disconnect()\r\n obsRef.current = null\r\n setStatus(\"disconnected\")\r\n }\r\n }, [url])\r\n\r\n const call = useCallback(\r\n async <T extends keyof OBSRequestTypes>(\r\n requestType: T,\r\n requestData?: OBSRequestTypes[T]\r\n ): Promise<OBSResponseTypes[T]> => {\r\n const obs = obsRef.current\r\n if (!obs) throw new Error(\"OBS not connected\")\r\n return obs.call(requestType, requestData)\r\n },\r\n []\r\n )\r\n\r\n return (\r\n <ObsContext.Provider value={{ status, call }}>\r\n {children}\r\n </ObsContext.Provider>\r\n )\r\n}\r\n","import { createContext, useContext } from \"react\"\r\nimport type { SparkleConfig } from \"./utils/config\"\r\n\r\nexport const SparkleConfigContext = createContext<SparkleConfig | null>(null)\r\n\r\nexport function useSparkleConfig(): SparkleConfig {\r\n const cfg = useContext(SparkleConfigContext)\r\n if (!cfg) {\r\n throw new Error(\"Sparkle hooks require SparkleProvider\")\r\n }\r\n return cfg\r\n}\r\n","import { useContext, useEffect, useMemo } from \"react\"\r\nimport { RealtimeContext, RealtimeValue } from \"../providers/realtimeProvider\"\r\n\r\nexport function useRealtime<T extends RealtimeValue>(\r\n key: string,\r\n defaultValue?: T\r\n): T[] {\r\n const context = useContext(RealtimeContext)\r\n\r\n const keys = useMemo(() => [key], [key])\r\n\r\n if (!context) {\r\n throw new Error(\"You must use useRealtime inside a SparkleProvider\")\r\n }\r\n\r\n useEffect(() => {\r\n context.subscribe([key])\r\n }, [keys])\r\n\r\n return keys.map((k) => (context.values[k] ?? defaultValue) as T)\r\n}\r\n","import { useEffect, useState } from \"react\"\n\nconst WIDGET_SESSION_REQ = \"sparkle-widget-session-request\"\nconst WIDGET_SESSION_RES = \"sparkle-widget-session-result\"\n\nfunction newRequestId(): string {\n if (typeof crypto !== \"undefined\" && \"randomUUID\" in crypto) {\n return crypto.randomUUID()\n }\n return `${Date.now()}-${Math.random().toString(16).slice(2)}`\n}\n\nfunction isEmbedded(): boolean {\n if (typeof window === \"undefined\") return false\n try {\n return window.self !== window.top\n } catch {\n return true\n }\n}\n\nfunction parseSessionMessage(d: unknown): {\n userId: string | null\n username: string | null\n profilePicture: string | null\n} {\n if (!d || typeof d !== \"object\") {\n return { userId: null, username: null, profilePicture: null }\n }\n const o = d as Record<string, unknown>\n const userId =\n typeof o.userId === \"string\" && o.userId.trim() ? o.userId : null\n const username =\n typeof o.username === \"string\" && o.username.trim() ? o.username : null\n const profilePicture =\n typeof o.profilePicture === \"string\" && o.profilePicture.trim()\n ? o.profilePicture\n : null\n return { userId, username, profilePicture }\n}\n\nfunction requestSessionViaParent(): Promise<{\n userId: string | null\n username: string | null\n profilePicture: string | null\n}> {\n if (typeof window === \"undefined\") {\n return Promise.resolve({\n userId: null,\n username: null,\n profilePicture: null,\n })\n }\n const target = window.top ?? window.parent\n if (!target || target === window) {\n return Promise.resolve({\n userId: null,\n username: null,\n profilePicture: null,\n })\n }\n return new Promise((resolve) => {\n const requestId = newRequestId()\n const onMsg = (ev: MessageEvent) => {\n const d = ev.data\n if (!d || d.type !== WIDGET_SESSION_RES || d.requestId !== requestId)\n return\n window.removeEventListener(\"message\", onMsg)\n window.clearTimeout(timer)\n resolve(parseSessionMessage(d))\n }\n const timer = window.setTimeout(() => {\n window.removeEventListener(\"message\", onMsg)\n resolve({ userId: null, username: null, profilePicture: null })\n }, 12_000)\n window.addEventListener(\"message\", onMsg)\n target.postMessage({ type: WIDGET_SESSION_REQ, requestId }, \"*\")\n })\n}\n\nasync function fetchSessionPayload(): Promise<{\n userId: string | null\n username: string | null\n profilePicture: string | null\n}> {\n const res = await fetch(\"/api/sparkle-widget-session\", {\n method: \"GET\",\n credentials: \"include\",\n })\n if (!res.ok) {\n return { userId: null, username: null, profilePicture: null }\n }\n const data = await res.json().catch(() => null)\n return parseSessionMessage(data)\n}\n\nexport type UseAuthResult = {\n /** Identifiant Twitch (`Account.accountId`), pas l’id interne Sparkle. */\n userId: string | null | undefined\n /** `User.name` Prisma (nom affiché OAuth). */\n username: string | null | undefined\n /** `User.image` Prisma (URL avatar). */\n profilePicture: string | null | undefined\n}\n\n/**\n * Profil session navigateur : `userId` = id Twitch (`Account.accountId` OAuth) ;\n * `username` / `profilePicture` depuis `User`. Champs à `undefined` pendant le chargement.\n */\nexport function useAuth(): UseAuthResult {\n const [userId, setUserId] = useState<string | null | undefined>(undefined)\n const [username, setUsername] = useState<string | null | undefined>(undefined)\n const [profilePicture, setProfilePicture] = useState<\n string | null | undefined\n >(undefined)\n\n useEffect(() => {\n let cancelled = false\n const run = async () => {\n const p = isEmbedded()\n ? await requestSessionViaParent()\n : await fetchSessionPayload()\n if (!cancelled) {\n setUserId(p.userId)\n setUsername(p.username)\n setProfilePicture(p.profilePicture)\n }\n }\n void run()\n return () => {\n cancelled = true\n }\n }, [])\n\n return { userId, username, profilePicture }\n}\n","import { useContext, useEffect, useMemo } from \"react\"\r\nimport { RealtimeContext } from \"../providers/realtimeProvider\"\r\nimport { EventTypeMap } from \"./hook\"\r\n\r\nexport const SparkleHookKeys = [\r\n \"notification.follow\",\r\n \"notification.cheer\",\r\n \"notification.tips\",\r\n \"notification.subscribe\",\r\n \"notification.subscribe.end\",\r\n \"notification.subscribe.gift\",\r\n \"notification.raid\",\r\n\r\n \"chat.message\",\r\n\r\n \"stream.online\",\r\n \"stream.offline\",\r\n \"stream.update\",\r\n\r\n \"reward.create\",\r\n \"reward.update\",\r\n \"reward.remove\",\r\n\r\n \"reward.redemption.claim\",\r\n \"reward.redemption.update\",\r\n\r\n \"poll.begin\",\r\n \"poll.progress\",\r\n \"poll.end\",\r\n\r\n \"prediction.begin\",\r\n \"prediction.progress\",\r\n \"prediction.lock\",\r\n \"prediction.end\",\r\n\r\n \"moderator.add\",\r\n \"moderator.remove\",\r\n \"moderator.ban\",\r\n \"moderator.unban\",\r\n \"moderator.shield_mode.begin\",\r\n] as const\r\n\r\nexport type SparkleHookKey = (typeof SparkleHookKeys)[number]\r\n\r\nexport function useHook<T extends keyof EventTypeMap>(\r\n hook: T\r\n): EventTypeMap[T] | undefined {\r\n const context = useContext(RealtimeContext)\r\n\r\n if (!context) {\r\n throw new Error(\"You must use useHook inside a SparkleProvider\")\r\n }\r\n\r\n useEffect(() => {\r\n context.listen(hook)\r\n }, [hook])\r\n\r\n return context.hooks[hook] as EventTypeMap[T]\r\n}\r\n","import { useCallback, useMemo } from \"react\"\r\nimport { useSparkleConfig } from \"../sparkle-config-context\"\r\n\r\nconst BRIDGE_TYPE = \"sparkle-invoke-task\"\r\nconst BRIDGE_RESULT = \"sparkle-invoke-task-result\"\r\n\r\ntype InvokeBridgeBody =\r\n | { kind: \"invoke\"; taskId: string; payload?: unknown }\r\n | {\r\n kind: \"invokeUntil\"\r\n taskId: string\r\n payload?: unknown\r\n delaySeconds: number\r\n }\r\n\r\nfunction newRequestId(): string {\r\n if (typeof crypto !== \"undefined\" && \"randomUUID\" in crypto) {\r\n return crypto.randomUUID()\r\n }\r\n return `${Date.now()}-${Math.random().toString(16).slice(2)}`\r\n}\r\n\r\nfunction bridgeInvoke(\r\n projectId: string,\r\n body: InvokeBridgeBody\r\n): Promise<boolean> {\r\n if (typeof window === \"undefined\") {\r\n return Promise.reject(new Error(\"useTask requires a browser environment\"))\r\n }\r\n const target = window.top ?? window.parent\r\n if (!target || target === window) {\r\n return Promise.reject(\r\n new Error(\r\n \"useTask: aucun parent — utilisez la preview éditeur ou une page intégrée.\"\r\n )\r\n )\r\n }\r\n\r\n return new Promise((resolve, reject) => {\r\n const requestId = newRequestId()\r\n const onMsg = (ev: MessageEvent) => {\r\n const d = ev.data\r\n if (!d || d.type !== BRIDGE_RESULT || d.requestId !== requestId) return\r\n window.removeEventListener(\"message\", onMsg)\r\n window.clearTimeout(timer)\r\n if (d.ok) resolve(Boolean(d.result))\r\n else reject(new Error(typeof d.error === \"string\" ? d.error : \"invoke_failed\"))\r\n }\r\n const timer = window.setTimeout(() => {\r\n window.removeEventListener(\"message\", onMsg)\r\n reject(new Error(\"useTask: délai dépassé\"))\r\n }, 45_000)\r\n window.addEventListener(\"message\", onMsg)\r\n target.postMessage({ type: BRIDGE_TYPE, requestId, projectId, ...body }, \"*\")\r\n })\r\n}\r\n\r\n/**\r\n * Dès que le widget tourne dans une iframe, un `fetch` vers `/api/...` n’emporte en général\r\n * pas la session (partitionnement / contexte imbriqué). Le relais `postMessage` → fenêtre\r\n * parente (même onglet, même origine) envoie les cookies comme une navigation classique.\r\n */\r\nfunction shouldUsePostMessageBridge(): boolean {\r\n if (typeof window === \"undefined\") return false\r\n try {\r\n return window.self !== window.top\r\n } catch {\r\n return true\r\n }\r\n}\r\n\r\nasync function fetchInvoke(\r\n projectId: string,\r\n body: InvokeBridgeBody\r\n): Promise<boolean> {\r\n const res = await fetch(`/api/projects/${projectId}/invoke-task`, {\r\n method: \"POST\",\r\n credentials: \"include\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify(body),\r\n })\r\n if (!res.ok) {\r\n const err = (await res.json().catch(() => null)) as { error?: string } | null\r\n throw new Error(err?.error ?? res.statusText)\r\n }\r\n return true\r\n}\r\n\r\n/**\r\n * Exécute une tâche Sparkle dans la runtime (équivalent à `ctx.invoke` / `ctx.invokeUntil`).\r\n * En preview Sandpack, les appels passent par `postMessage` vers l’éditeur (session).\r\n */\r\nexport function useTask() {\r\n const config = useSparkleConfig()\r\n const projectId = config.projectId\r\n\r\n const invoke = useCallback(\r\n async (taskId: string, payload?: unknown) => {\r\n if (!projectId?.trim()) {\r\n throw new Error(\"useTask: renseignez `config.projectId` sur SparkleProvider.\")\r\n }\r\n const body: InvokeBridgeBody = { kind: \"invoke\", taskId, payload }\r\n if (shouldUsePostMessageBridge()) {\r\n return bridgeInvoke(projectId, body)\r\n }\r\n return fetchInvoke(projectId, body)\r\n },\r\n [projectId]\r\n )\r\n\r\n const invokeUntil = useCallback(\r\n async (taskId: string, delaySeconds: number, payload?: unknown) => {\r\n if (!projectId?.trim()) {\r\n throw new Error(\"useTask: renseignez `config.projectId` sur SparkleProvider.\")\r\n }\r\n const body: InvokeBridgeBody = {\r\n kind: \"invokeUntil\",\r\n taskId,\r\n payload,\r\n delaySeconds,\r\n }\r\n if (shouldUsePostMessageBridge()) {\r\n return bridgeInvoke(projectId, body)\r\n }\r\n return fetchInvoke(projectId, body)\r\n },\r\n [projectId]\r\n )\r\n\r\n return useMemo(() => ({ invoke, invokeUntil }), [invoke, invokeUntil])\r\n}\r\n","import { useCallback, useMemo } from \"react\"\r\nimport { useSparkleConfig } from \"../sparkle-config-context\"\r\nimport { createTwitchGetClient } from \"../twitch/create-twitch-get-client\"\r\nimport type { TwitchGetClient } from \"../twitch/twitch-get-types\"\r\n\r\nexport type { TwitchGetClient } from \"../twitch/twitch-get-types\"\r\n\r\nconst BRIDGE_TYPE = \"sparkle-twitch-get\"\r\nconst BRIDGE_RESULT = \"sparkle-twitch-get-result\"\r\n\r\nexport type TwitchGetApiBody = {\r\n path: string[]\r\n args?: unknown[]\r\n}\r\n\r\nfunction newRequestId(): string {\r\n if (typeof crypto !== \"undefined\" && \"randomUUID\" in crypto) {\r\n return crypto.randomUUID()\r\n }\r\n return `${Date.now()}-${Math.random().toString(16).slice(2)}`\r\n}\r\n\r\nfunction shouldUsePostMessageBridge(): boolean {\r\n if (typeof window === \"undefined\") return false\r\n try {\r\n return window.self !== window.top\r\n } catch {\r\n return true\r\n }\r\n}\r\n\r\nfunction bridgeTwitchGet(\r\n projectId: string,\r\n body: TwitchGetApiBody\r\n): Promise<unknown> {\r\n if (typeof window === \"undefined\") {\r\n return Promise.reject(new Error(\"useTwitch requires a browser environment\"))\r\n }\r\n const target = window.top ?? window.parent\r\n if (!target || target === window) {\r\n return Promise.reject(\r\n new Error(\r\n \"useTwitch: aucun parent — utilisez la preview éditeur ou une page intégrée.\"\r\n )\r\n )\r\n }\r\n\r\n return new Promise((resolve, reject) => {\r\n const requestId = newRequestId()\r\n const onMsg = (ev: MessageEvent) => {\r\n const d = ev.data\r\n if (!d || d.type !== BRIDGE_RESULT || d.requestId !== requestId) return\r\n window.removeEventListener(\"message\", onMsg)\r\n window.clearTimeout(timer)\r\n if (d.ok) resolve(d.result)\r\n else\r\n reject(\r\n new Error(typeof d.error === \"string\" ? d.error : \"twitch_get_failed\")\r\n )\r\n }\r\n const timer = window.setTimeout(() => {\r\n window.removeEventListener(\"message\", onMsg)\r\n reject(new Error(\"useTwitch: délai dépassé\"))\r\n }, 45_000)\r\n window.addEventListener(\"message\", onMsg)\r\n target.postMessage(\r\n { type: BRIDGE_TYPE, requestId, projectId, ...body },\r\n \"*\"\r\n )\r\n })\r\n}\r\n\r\nasync function fetchTwitchGet(\r\n projectId: string,\r\n body: TwitchGetApiBody\r\n): Promise<unknown> {\r\n const res = await fetch(\r\n `/api/projects/${encodeURIComponent(projectId)}/twitch-get`,\r\n {\r\n method: \"POST\",\r\n credentials: \"include\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify(body),\r\n }\r\n )\r\n if (!res.ok) {\r\n const err = (await res.json().catch(() => null)) as { error?: string } | null\r\n throw new Error(err?.error ?? res.statusText)\r\n }\r\n const json = (await res.json()) as { result?: unknown }\r\n return json.result\r\n}\r\n\r\n/**\r\n * Client Twitch en lecture seule (Twurple via le backend du projet).\r\n * Même surface GET que `ctx.twitch` dans les handlers runtime — pas les mutations.\r\n *\r\n * Requiert `config.projectId` sur `SparkleProvider`.\r\n */\r\nexport function useTwitch(): TwitchGetClient {\r\n const config = useSparkleConfig()\r\n const projectId = config.projectId\r\n\r\n const invoke = useCallback(\r\n async (namespace: string, method: string, args: unknown[]) => {\r\n if (!projectId?.trim()) {\r\n throw new Error(\r\n \"useTwitch: renseignez `config.projectId` sur SparkleProvider.\"\r\n )\r\n }\r\n const body: TwitchGetApiBody = {\r\n path: [namespace, method],\r\n args,\r\n }\r\n if (shouldUsePostMessageBridge()) {\r\n return bridgeTwitchGet(projectId, body)\r\n }\r\n return fetchTwitchGet(projectId, body)\r\n },\r\n [projectId]\r\n )\r\n\r\n return useMemo(() => createTwitchGetClient(invoke), [invoke])\r\n}\r\n","import type { TwitchGetClient } from \"./twitch-get-types\"\n\nexport type TwitchGetInvoker = (\n namespace: string,\n method: string,\n args: unknown[]\n) => Promise<unknown>\n\nexport function createTwitchGetClient(\n invoke: TwitchGetInvoker\n): TwitchGetClient {\n const call =\n (ns: string, method: string) =>\n (...args: unknown[]) =>\n invoke(ns, method, args)\n\n return {\n bits: {\n getLeaderboard: call(\"bits\", \"getLeaderboard\"),\n },\n channelPoints: {\n getCustomRewards: call(\"channelPoints\", \"getCustomRewards\"),\n getCustomRewardsByIds: call(\"channelPoints\", \"getCustomRewardsByIds\"),\n getCustomRewardById: call(\"channelPoints\", \"getCustomRewardById\"),\n getRedemptionsByIds: call(\"channelPoints\", \"getRedemptionsByIds\"),\n getRedemptionById: call(\"channelPoints\", \"getRedemptionById\"),\n getRedemptions: call(\"channelPoints\", \"getRedemptions\"),\n },\n channel: {\n getChannelInfoById: call(\"channel\", \"getChannelInfoById\"),\n getFollowDate: call(\"channel\", \"getFollowDate\"),\n getChannelEditors: call(\"channel\", \"getChannelEditors\"),\n getVips: call(\"channel\", \"getVips\"),\n checkVipForUsers: call(\"channel\", \"checkVipForUsers\"),\n checkVipForUser: call(\"channel\", \"checkVipForUser\"),\n getChannelFollowerCount: call(\"channel\", \"getChannelFollowerCount\"),\n },\n chat: {\n getChatters: call(\"chat\", \"getChatters\"),\n getSettings: call(\"chat\", \"getSettings\"),\n getSettingsPrivileged: call(\"chat\", \"getSettingsPrivileged\"),\n getColorsForUsers: call(\"chat\", \"getColorsForUsers\"),\n getColorForUser: call(\"chat\", \"getColorForUser\"),\n },\n clips: {\n getClips: call(\"clips\", \"getClips\"),\n getClipById: call(\"clips\", \"getClipById\"),\n getClipsByIds: call(\"clips\", \"getClipsByIds\"),\n },\n goals: {\n getGoals: call(\"goals\", \"getGoals\"),\n },\n polls: {\n getPolls: call(\"polls\", \"getPolls\"),\n getPollsByIds: call(\"polls\", \"getPollsByIds\"),\n getPollById: call(\"polls\", \"getPollById\"),\n },\n predictions: {\n getPredictions: call(\"predictions\", \"getPredictions\"),\n getPredictionsByIds: call(\"predictions\", \"getPredictionsByIds\"),\n getPredictionById: call(\"predictions\", \"getPredictionById\"),\n },\n streams: {\n getStream: call(\"streams\", \"getStream\"),\n getStreamMarkers: call(\"streams\", \"getStreamMarkers\"),\n },\n subscriptions: {\n getSubscriptions: call(\"subscriptions\", \"getSubscriptions\"),\n getSubscriptionsForUsers: call(\n \"subscriptions\",\n \"getSubscriptionsForUsers\"\n ),\n getSubscriptionForUser: call(\"subscriptions\", \"getSubscriptionForUser\"),\n checkUserSubscription: call(\"subscriptions\", \"checkUserSubscription\"),\n },\n moderation: {\n getBanned: call(\"moderation\", \"getBanned\"),\n checkBan: call(\"moderation\", \"checkBan\"),\n getModerators: call(\"moderation\", \"getModerators\"),\n checkModerator: call(\"moderation\", \"checkModerator\"),\n getBlockedTerms: call(\"moderation\", \"getBlockedTerms\"),\n },\n utils: {\n getUserById: call(\"utils\", \"getUserById\"),\n getUserByName: call(\"utils\", \"getUserByName\"),\n getUserByIds: call(\"utils\", \"getUserByIds\"),\n getUserByNames: call(\"utils\", \"getUserByNames\"),\n },\n } as unknown as TwitchGetClient\n}\n","import { useCallback, useContext, useEffect, useRef, useState } from \"react\"\r\n\r\nimport { RealtimeContext } from \"../providers/realtimeProvider\"\r\nimport { useSparkleConfig } from \"../sparkle-config-context\"\r\nimport {\r\n coerceChannelLabelsSnapshot,\r\n emptyChannelLabels,\r\n type ChannelLabelsSnapshot,\r\n} from \"../types/labels\"\r\n\r\nconst LABELS_SNAPSHOT_KEY = \"labels:snapshot\"\r\nconst BRIDGE_TYPE = \"sparkle-labels-get\"\r\nconst BRIDGE_RESULT = \"sparkle-labels-get-result\"\r\n\r\nfunction newRequestId(): string {\r\n if (typeof crypto !== \"undefined\" && \"randomUUID\" in crypto) {\r\n return crypto.randomUUID()\r\n }\r\n return `${Date.now()}-${Math.random().toString(16).slice(2)}`\r\n}\r\n\r\nfunction isEmbeddedFrame(): boolean {\r\n if (typeof window === \"undefined\") return false\r\n try {\r\n return window.self !== window.top\r\n } catch {\r\n return true\r\n }\r\n}\r\n\r\nfunction bridgeLabelsGet(projectId: string): Promise<ChannelLabelsSnapshot> {\r\n if (typeof window === \"undefined\") {\r\n return Promise.reject(new Error(\"useLabels requires a browser environment\"))\r\n }\r\n const target = window.top ?? window.parent\r\n if (!target || target === window) {\r\n return Promise.reject(\r\n new Error(\r\n \"useLabels: aucun parent — utilisez la preview éditeur ou une page intégrée.\"\r\n )\r\n )\r\n }\r\n\r\n return new Promise((resolve, reject) => {\r\n const requestId = newRequestId()\r\n const onMsg = (ev: MessageEvent) => {\r\n const d = ev.data\r\n if (!d || d.type !== BRIDGE_RESULT || d.requestId !== requestId) return\r\n window.removeEventListener(\"message\", onMsg)\r\n window.clearTimeout(timer)\r\n if (d.ok && d.labels) resolve(d.labels as ChannelLabelsSnapshot)\r\n else\r\n reject(\r\n new Error(typeof d.error === \"string\" ? d.error : \"labels_get_failed\")\r\n )\r\n }\r\n const timer = window.setTimeout(() => {\r\n window.removeEventListener(\"message\", onMsg)\r\n reject(new Error(\"useLabels: délai dépassé\"))\r\n }, 45_000)\r\n window.addEventListener(\"message\", onMsg)\r\n target.postMessage({ type: BRIDGE_TYPE, requestId, projectId }, \"*\")\r\n })\r\n}\r\n\r\nasync function fetchLabels(projectId: string): Promise<ChannelLabelsSnapshot> {\r\n const res = await fetch(\r\n `/api/projects/${encodeURIComponent(projectId)}/labels`,\r\n {\r\n method: \"GET\",\r\n credentials: \"include\",\r\n }\r\n )\r\n if (!res.ok) {\r\n const err = (await res.json().catch(() => null)) as { error?: string } | null\r\n throw new Error(err?.error ?? res.statusText)\r\n }\r\n const json = (await res.json()) as { labels?: ChannelLabelsSnapshot }\r\n return json.labels ?? emptyChannelLabels()\r\n}\r\n\r\nasync function loadLabelsFallback(\r\n projectId: string,\r\n useBridge: boolean\r\n): Promise<ChannelLabelsSnapshot> {\r\n if (useBridge) return bridgeLabelsGet(projectId)\r\n return fetchLabels(projectId)\r\n}\r\n\r\n/**\r\n * Agrégats chaîne (session, total, latest, recent) — même données que `ctx.labels.get()`.\r\n * Poussés en temps réel via WebSocket (`labels:snapshot`, comme `useRealtime`).\r\n * Requiert `config.projectId` et `config.userId` sur `SparkleProvider`.\r\n */\r\nexport function useLabels(): ChannelLabelsSnapshot {\r\n const config = useSparkleConfig()\r\n const projectId = config.projectId?.trim() ?? \"\"\r\n const userId = config.userId?.trim() ?? \"\"\r\n const realtime = useContext(RealtimeContext)\r\n const useRealtimeLabels = Boolean(userId)\r\n const useBridge = isEmbeddedFrame() && !useRealtimeLabels\r\n\r\n const [labels, setLabels] = useState<ChannelLabelsSnapshot>(emptyChannelLabels)\r\n const requestIdRef = useRef(0)\r\n const realtimeSnapshot = realtime.values[LABELS_SNAPSHOT_KEY]\r\n\r\n useEffect(() => {\r\n if (!useRealtimeLabels) return\r\n realtime.subscribe([LABELS_SNAPSHOT_KEY])\r\n }, [useRealtimeLabels, realtime])\r\n\r\n useEffect(() => {\r\n if (!useRealtimeLabels) return\r\n const parsed = coerceChannelLabelsSnapshot(realtimeSnapshot)\r\n if (parsed) setLabels(parsed)\r\n }, [useRealtimeLabels, realtimeSnapshot])\r\n\r\n const refresh = useCallback(async () => {\r\n if (!projectId) return\r\n const requestId = ++requestIdRef.current\r\n try {\r\n const next = await loadLabelsFallback(projectId, useBridge)\r\n if (requestId === requestIdRef.current) {\r\n setLabels(next)\r\n }\r\n } catch {\r\n // Conserve la dernière valeur connue.\r\n }\r\n }, [projectId, useBridge])\r\n\r\n useEffect(() => {\r\n if (!projectId) {\r\n setLabels(emptyChannelLabels())\r\n return\r\n }\r\n\r\n if (useRealtimeLabels) {\r\n const parsed = coerceChannelLabelsSnapshot(realtimeSnapshot)\r\n if (!parsed) void refresh()\r\n return\r\n }\r\n\r\n void refresh()\r\n }, [projectId, refresh, useRealtimeLabels, realtimeSnapshot])\r\n\r\n return labels\r\n}\r\n","export type ChannelLabelCounts = {\r\n followers: number\r\n subscribers: number\r\n cheers: number\r\n tips: number\r\n}\r\n\r\nexport type ChannelLabelLatest = {\r\n followers: string\r\n subscribers: string\r\n cheers: { amount: number; user: string }\r\n tips: { amount: number; user: string }\r\n}\r\n\r\nexport type ChannelLabelRecent = {\r\n followers: string[]\r\n subscribers: string[]\r\n cheers: string[]\r\n tips: string[]\r\n}\r\n\r\nexport type ChannelLabelsSnapshot = {\r\n session: ChannelLabelCounts\r\n total: ChannelLabelCounts\r\n latest: ChannelLabelLatest\r\n recent: ChannelLabelRecent\r\n}\r\n\r\nfunction parseCounts(raw: unknown): ChannelLabelsSnapshot[\"session\"] | null {\r\n if (!raw || typeof raw !== \"object\") return null\r\n const record = raw as Record<string, unknown>\r\n const read = (key: keyof ChannelLabelsSnapshot[\"session\"]) => {\r\n const value = Number(record[key])\r\n return Number.isFinite(value) ? Math.max(0, Math.floor(value)) : 0\r\n }\r\n return {\r\n followers: read(\"followers\"),\r\n subscribers: read(\"subscribers\"),\r\n cheers: read(\"cheers\"),\r\n tips: read(\"tips\"),\r\n }\r\n}\r\n\r\nfunction parseLatest(raw: unknown): ChannelLabelsSnapshot[\"latest\"] | null {\r\n if (!raw || typeof raw !== \"object\") return null\r\n const record = raw as Record<string, unknown>\r\n const cheers =\r\n record.cheers && typeof record.cheers === \"object\"\r\n ? (record.cheers as Record<string, unknown>)\r\n : null\r\n const tips =\r\n record.tips && typeof record.tips === \"object\"\r\n ? (record.tips as Record<string, unknown>)\r\n : null\r\n\r\n return {\r\n followers: typeof record.followers === \"string\" ? record.followers : \"\",\r\n subscribers:\r\n typeof record.subscribers === \"string\" ? record.subscribers : \"\",\r\n cheers: {\r\n amount:\r\n cheers && Number.isFinite(Number(cheers.amount))\r\n ? Math.max(0, Math.floor(Number(cheers.amount)))\r\n : 0,\r\n user: cheers && typeof cheers.user === \"string\" ? cheers.user : \"\",\r\n },\r\n tips: {\r\n amount:\r\n tips && Number.isFinite(Number(tips.amount)) ? Number(tips.amount) : 0,\r\n user: tips && typeof tips.user === \"string\" ? tips.user : \"\",\r\n },\r\n }\r\n}\r\n\r\nfunction parseRecentList(raw: unknown): string[] {\r\n if (!Array.isArray(raw)) return []\r\n return raw.filter((item): item is string => typeof item === \"string\")\r\n}\r\n\r\nexport function coerceChannelLabelsSnapshot(\r\n raw: unknown\r\n): ChannelLabelsSnapshot | null {\r\n if (!raw || typeof raw !== \"object\") return null\r\n\r\n const record = raw as Record<string, unknown>\r\n const session = parseCounts(record.session)\r\n const total = parseCounts(record.total)\r\n const latest = parseLatest(record.latest)\r\n const recentRaw =\r\n record.recent && typeof record.recent === \"object\"\r\n ? (record.recent as Record<string, unknown>)\r\n : null\r\n\r\n if (!session || !total || !latest || !recentRaw) return null\r\n\r\n return {\r\n session,\r\n total,\r\n latest,\r\n recent: {\r\n followers: parseRecentList(recentRaw.followers),\r\n subscribers: parseRecentList(recentRaw.subscribers),\r\n cheers: parseRecentList(recentRaw.cheers),\r\n tips: parseRecentList(recentRaw.tips),\r\n },\r\n }\r\n}\r\n\r\nexport const emptyChannelLabels = (): ChannelLabelsSnapshot => ({\r\n session: {\r\n followers: 0,\r\n subscribers: 0,\r\n cheers: 0,\r\n tips: 0,\r\n },\r\n total: {\r\n followers: 0,\r\n subscribers: 0,\r\n cheers: 0,\r\n tips: 0,\r\n },\r\n latest: {\r\n followers: \"\",\r\n subscribers: \"\",\r\n cheers: { amount: 0, user: \"\" },\r\n tips: { amount: 0, user: \"\" },\r\n },\r\n recent: {\r\n followers: [],\r\n subscribers: [],\r\n cheers: [],\r\n tips: [],\r\n },\r\n})\r\n","import { useCallback, useEffect, useRef, useState } from \"react\"\nimport { useSparkleConfig } from \"../sparkle-config-context\"\n\nexport interface SpotifyTrack {\n isPlaying: boolean\n title: string | null\n artist: string | null\n album: string | null\n coverUrl: string | null\n durationMs: number\n progressMs: number\n}\n\nexport interface UseSpotifyResult {\n track: SpotifyTrack | null\n loading: boolean\n error: Error | null\n refresh: () => Promise<void>\n}\n\nfunction newRequestId(): string {\n if (typeof crypto !== \"undefined\" && \"randomUUID\" in crypto) {\n return crypto.randomUUID()\n }\n return `${Date.now()}-${Math.random().toString(16).slice(2)}`\n}\n\nfunction isEmbeddedFrame(): boolean {\n if (typeof window === \"undefined\") return false\n try {\n return window.self !== window.top\n } catch {\n return true\n }\n}\n\nfunction bridgeSpotifyGet(projectId: string): Promise<SpotifyTrack | null> {\n if (typeof window === \"undefined\") {\n return Promise.reject(new Error(\"useSpotify requires a browser environment\"))\n }\n const target = window.top ?? window.parent\n if (!target || target === window) {\n return Promise.reject(\n new Error(\n \"useSpotify: aucun parent — utilisez la preview éditeur ou une page intégrée.\"\n )\n )\n }\n\n return new Promise((resolve, reject) => {\n const requestId = newRequestId()\n const onMsg = (ev: MessageEvent) => {\n const d = ev.data\n if (!d || d.type !== \"sparkle-spotify-get-result\" || d.requestId !== requestId) return\n window.removeEventListener(\"message\", onMsg)\n window.clearTimeout(timer)\n if (d.ok) resolve(d.track as SpotifyTrack | null)\n else\n reject(\n new Error(typeof d.error === \"string\" ? d.error : \"spotify_get_failed\")\n )\n }\n const timer = window.setTimeout(() => {\n window.removeEventListener(\"message\", onMsg)\n reject(new Error(\"useSpotify: délai dépassé\"))\n }, 45_000)\n window.addEventListener(\"message\", onMsg)\n target.postMessage({ type: \"sparkle-spotify-get\", requestId, projectId }, \"*\")\n })\n}\n\nasync function fetchSpotify(projectId: string): Promise<SpotifyTrack | null> {\n const res = await fetch(\n `/api/projects/${encodeURIComponent(projectId)}/spotify`,\n {\n method: \"GET\",\n credentials: \"include\",\n }\n )\n if (!res.ok) {\n const err = (await res.json().catch(() => null)) as { error?: string } | null\n throw new Error(err?.error ?? res.statusText)\n }\n const json = (await res.json()) as { track?: SpotifyTrack | null }\n return json.track ?? null\n}\n\nexport function useSpotify(options?: { intervalMs?: number }): UseSpotifyResult {\n const config = useSparkleConfig()\n const projectId = config.projectId?.trim() ?? \"\"\n const intervalMs = options?.intervalMs ?? 5000\n const useBridge = isEmbeddedFrame()\n\n const [track, setTrack] = useState<SpotifyTrack | null>(null)\n const [loading, setLoading] = useState(true)\n const [error, setError] = useState<Error | null>(null)\n\n const requestIdRef = useRef(0)\n\n const refresh = useCallback(async () => {\n if (!projectId) {\n setTrack(null)\n setLoading(false)\n return\n }\n\n const requestId = ++requestIdRef.current\n try {\n const data = useBridge\n ? await bridgeSpotifyGet(projectId)\n : await fetchSpotify(projectId)\n\n if (requestId === requestIdRef.current) {\n setTrack(data)\n setError(null)\n }\n } catch (err) {\n if (requestId === requestIdRef.current) {\n setError(err instanceof Error ? err : new Error(String(err)))\n }\n } finally {\n if (requestId === requestIdRef.current) {\n setLoading(false)\n }\n }\n }, [projectId, useBridge])\n\n useEffect(() => {\n void refresh()\n }, [refresh])\n\n useEffect(() => {\n if (intervalMs <= 0 || !projectId) return\n\n const timer = setInterval(() => {\n void refresh()\n }, intervalMs)\n\n return () => clearInterval(timer)\n }, [refresh, intervalMs, projectId])\n\n return { track, loading, error, refresh }\n}\n","import { useCallback } from \"react\"\nimport { useSparkleConfig } from \"../sparkle-config-context\"\n\nexport type ElevenLabsVoiceSettings = {\n stability?: number\n similarity_boost?: number\n style?: number\n use_speaker_boost?: boolean\n}\n\nexport type ElevenLabsTtsOptions = {\n voiceId: string\n modelId?: string\n voice_settings?: ElevenLabsVoiceSettings\n}\n\nexport const ELEVENLABS_PRESET_VOICES = [\n { id: \"adam\", voiceId: \"pNInz6obpgDQGcFmaJgB\", name: \"Adam - Dominant, Firm\" },\n { id: \"alice\", voiceId: \"Xb7hH8MSUJpSbSDYk0k2\", name: \"Alice - Clear, Engaging Educator\" },\n { id: \"andy-lin\", voiceId: \"h1i3CVVBUuF6s46cxUGG\", name: \"Andy Lin - Gentle and Friendly Narrator\" },\n { id: \"anika\", voiceId: \"ecp3DWciuUyW7BYM7II1\", name: \"Anika - Sweet and Lively\" },\n { id: \"bella\", voiceId: \"hpp4J3VqNfWAUOO0d1Us\", name: \"Bella - Professional, Bright, Warm\" },\n { id: \"bill\", voiceId: \"pqHfZKP75CvOlQylNhV4\", name: \"Bill - Wise, Mature, Balanced\" },\n { id: \"brian\", voiceId: \"nPczCjzI2devNBz1zQrb\", name: \"Brian - Deep, Resonant and Comforting\" },\n { id: \"callum\", voiceId: \"N2lVS1w4EtoT3dr4eOWO\", name: \"Callum - Husky Trickster\" },\n { id: \"charlie\", voiceId: \"IKne3meq5aSn9XLyUdCD\", name: \"Charlie - Deep, Confident, Energetic\" },\n { id: \"chris\", voiceId: \"iP95p4xoKVk53GoZ742B\", name: \"Chris - Charming, Down-to-Earth\" },\n { id: \"daniel\", voiceId: \"onwK4e9ZLuTAKqWW03F9\", name: \"Daniel - Steady Broadcaster\" },\n { id: \"eric\", voiceId: \"cjVigY5qzO86Huf0OWal\", name: \"Eric - Smooth, Trustworthy\" },\n { id: \"george\", voiceId: \"JBFqnCBsd6RMkjVDRZzb\", name: \"George - Warm, Captivating Storyteller\" },\n { id: \"harry\", voiceId: \"SOYHLrjzK2X1ezoPC6cr\", name: \"Harry - Fierce Warrior\" },\n { id: \"jessica\", voiceId: \"r1KmysJdVYZjJCm4mL3b\", name: \"Jessica - Playful, Bright, Warm\" },\n { id: \"keshavi\", voiceId: \"Ek86tj0PS0XTYchY9Ody\", name: \"Keshavi - Clear & Human AI Assistant\" },\n { id: \"laura\", voiceId: \"FGY2WhTYpPnrIDTdsKH5\", name: \"Laura - Enthusiast, Quirky Attitude\" },\n { id: \"liam\", voiceId: \"TX3LPaxmHKxFdv7VOQHJ\", name: \"Liam - Energetic, Social Media Creator\" },\n { id: \"lily\", voiceId: \"pFZP5JQG7iQjIQuC4Bku\", name: \"Lily - Velvety Actress\" },\n { id: \"matilda\", voiceId: \"XrExE9yKIg1WjnnlVkGX\", name: \"Matilda - Knowledgable, Professional\" },\n { id: \"river\", voiceId: \"SAz9YHcvj6GT2YYXdXww\", name: \"River - Relaxed, Neutral, Informative\" },\n { id: \"roger\", voiceId: \"CwhRBWXzGAHq8TQ4Fs17\", name: \"Roger - Laid-Back, Casual, Resonant\" },\n { id: \"sarah\", voiceId: \"EXAVITQu4vr4xnSDxMaL\", name: \"Sarah - Mature, Reassuring, Confident\" },\n { id: \"will\", voiceId: \"bIHbv24MWmeRgasZH58o\", name: \"Will - Relaxed Optimist\" },\n] as const\n\nexport type ElevenLabsPresetVoiceId = (typeof ELEVENLABS_PRESET_VOICES)[number][\"id\"]\n\nexport type ElevenLabsSpeakOptions = {\n voice: ElevenLabsPresetVoiceId | (string & {})\n voice_settings?: ElevenLabsVoiceSettings\n}\n\nfunction newRequestId(): string {\n if (typeof crypto !== \"undefined\" && \"randomUUID\" in crypto) {\n return crypto.randomUUID()\n }\n return `${Date.now()}-${Math.random().toString(16).slice(2)}`\n}\n\nfunction isEmbeddedFrame(): boolean {\n if (typeof window === \"undefined\") return false\n try {\n return window.self !== window.top\n } catch {\n return true\n }\n}\n\nfunction bridgeElevenLabsSpeak(\n userId: string,\n token: string,\n text: string,\n options: ElevenLabsTtsOptions\n): Promise<HTMLAudioElement> {\n if (typeof window === \"undefined\") {\n return Promise.reject(new Error(\"useVoices requires a browser environment\"))\n }\n const target = window.top ?? window.parent\n if (!target || target === window) {\n return Promise.reject(\n new Error(\"useVoices: aucun parent — utilisez la preview éditeur ou une page intégrée.\")\n )\n }\n\n return new Promise((resolve, reject) => {\n const requestId = newRequestId()\n const onMsg = (ev: MessageEvent) => {\n const d = ev.data\n if (!d || d.type !== \"sparkle-elevenlabs-speak-result\" || d.requestId !== requestId) return\n window.removeEventListener(\"message\", onMsg)\n window.clearTimeout(timer)\n if (d.ok) {\n const blob = new Blob([d.buffer], { type: d.contentType })\n const url = URL.createObjectURL(blob)\n const audio = new Audio(url)\n audio.addEventListener(\n \"canplaythrough\",\n () => {\n audio.play().catch(reject)\n resolve(audio)\n },\n { once: true }\n )\n audio.addEventListener(\"error\", () => {\n reject(new Error(\"Failed to load audio\"))\n })\n audio.load()\n } else {\n reject(new Error(typeof d.error === \"string\" ? d.error : \"elevenlabs_speak_failed\"))\n }\n }\n const timer = window.setTimeout(() => {\n window.removeEventListener(\"message\", onMsg)\n reject(new Error(\"useVoices: délai dépassé\"))\n }, 45_000)\n window.addEventListener(\"message\", onMsg)\n target.postMessage({ type: \"sparkle-elevenlabs-speak\", requestId, userId, token, text, options }, \"*\")\n })\n}\n\nasync function fetchElevenLabsSpeak(\n userId: string,\n token: string,\n text: string,\n options: ElevenLabsTtsOptions\n): Promise<HTMLAudioElement> {\n const params = new URLSearchParams()\n if (userId) params.set(\"userId\", userId)\n if (token) params.set(\"k\", token)\n\n const res = await fetch(`/api/obs/alerts/elevenlabs-tts?${params.toString()}`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n text,\n ...options,\n }),\n })\n\n if (!res.ok) {\n throw new Error(`TTS Request failed with status ${res.status}`)\n }\n\n const blob = await res.blob()\n const url = URL.createObjectURL(blob)\n const audio = new Audio(url)\n\n return new Promise((resolve, reject) => {\n audio.addEventListener(\n \"canplaythrough\",\n () => {\n audio.play().catch(reject)\n resolve(audio)\n },\n { once: true }\n )\n\n audio.addEventListener(\"error\", () => {\n reject(new Error(\"Failed to load audio\"))\n })\n\n audio.load()\n })\n}\n\nexport function useVoices() {\n const config = useSparkleConfig()\n const userId = config.userId?.trim() ?? \"\"\n const apiKey = config.apiKey?.trim() ?? \"\"\n const useBridge = isEmbeddedFrame()\n\n const speak = useCallback(\n async (\n text: string,\n options: ElevenLabsSpeakOptions\n ): Promise<HTMLAudioElement> => {\n const preset = ELEVENLABS_PRESET_VOICES.find((v) => v.id === options.voice)\n const voiceId = preset ? preset.voiceId : options.voice\n\n const apiOptions: ElevenLabsTtsOptions = {\n voiceId,\n voice_settings: options.voice_settings,\n }\n\n if (useBridge) {\n return bridgeElevenLabsSpeak(userId, apiKey, text, apiOptions)\n } else {\n return fetchElevenLabsSpeak(userId, apiKey, text, apiOptions)\n }\n },\n [userId, apiKey, useBridge]\n )\n\n return { speak }\n}\n\n","import { useCallback, useState } from \"react\"\nimport { useSparkleConfig } from \"../sparkle-config-context\"\n\nexport type GenerateOptions = {\n model?: \"gemini-3.1-flash-lite\" | \"gemini-3.5-flash\"\n}\n\nfunction newRequestId(): string {\n if (typeof crypto !== \"undefined\" && \"randomUUID\" in crypto) {\n return crypto.randomUUID()\n }\n return `${Date.now()}-${Math.random().toString(16).slice(2)}`\n}\n\nfunction isEmbeddedFrame(): boolean {\n if (typeof window === \"undefined\") return false\n try {\n return window.self !== window.top\n } catch {\n return true\n }\n}\n\nfunction bridgeAiGenerate(\n userId: string,\n token: string,\n prompt: string,\n model?: string\n): Promise<string> {\n if (typeof window === \"undefined\") {\n return Promise.reject(new Error(\"useGenerate requires a browser environment\"))\n }\n const target = window.top ?? window.parent\n if (!target || target === window) {\n return Promise.reject(\n new Error(\n \"useGenerate: aucun parent — utilisez la preview éditeur ou une page intégrée.\"\n )\n )\n }\n\n return new Promise((resolve, reject) => {\n const requestId = newRequestId()\n const onMsg = (ev: MessageEvent) => {\n const d = ev.data\n if (!d || d.type !== \"sparkle-ai-generate-result\" || d.requestId !== requestId) return\n window.removeEventListener(\"message\", onMsg)\n window.clearTimeout(timer)\n if (d.ok) resolve(d.text as string)\n else reject(new Error(typeof d.error === \"string\" ? d.error : \"ai_generate_failed\"))\n }\n const timer = window.setTimeout(() => {\n window.removeEventListener(\"message\", onMsg)\n reject(new Error(\"useGenerate: délai dépassé\"))\n }, 45_000)\n window.addEventListener(\"message\", onMsg)\n target.postMessage({ type: \"sparkle-ai-generate\", requestId, userId, token, prompt, model }, \"*\")\n })\n}\n\nasync function fetchAiGenerate(\n userId: string,\n token: string,\n prompt: string,\n model?: string\n): Promise<string> {\n const params = new URLSearchParams()\n if (userId) params.set(\"userId\", userId)\n if (token) params.set(\"k\", token)\n\n const res = await fetch(`/api/obs/ai-generate?${params.toString()}`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ prompt, model }),\n })\n\n if (!res.ok) {\n const errData = await res.json().catch(() => null)\n throw new Error(errData?.error || `Generation failed with status ${res.status}`)\n }\n\n const data = await res.json()\n return data.text\n}\n\nexport function useGenerate() {\n const config = useSparkleConfig()\n const userId = config.userId?.trim() ?? \"\"\n const apiKey = config.apiKey?.trim() ?? \"\"\n const useBridge = isEmbeddedFrame()\n\n const [isGenerating, setIsGenerating] = useState(false)\n const [error, setError] = useState<Error | null>(null)\n\n const generateText = useCallback(\n async (prompt: string, options?: GenerateOptions): Promise<string> => {\n setIsGenerating(true)\n setError(null)\n\n try {\n const text = useBridge\n ? await bridgeAiGenerate(userId, apiKey, prompt, options?.model)\n : await fetchAiGenerate(userId, apiKey, prompt, options?.model)\n \n return text\n } catch (err) {\n const e = err instanceof Error ? err : new Error(\"Unknown error generating text\")\n setError(e)\n throw e\n } finally {\n setIsGenerating(false)\n }\n },\n [userId, apiKey, useBridge]\n )\n\n return {\n generateText,\n isGenerating,\n error,\n }\n}\n"],"mappings":"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","names":["React","useMemo","useEffect","useState","React","createContext","SparkleAuthContext","AuthProvider","children","config","React","useCallback","useContext","useRef","createContext","useEffect","useState","EventEmitter","z","subscribeSchema","hookSchema","unsubscribeSchema","updateSchema","messageSchema","WebSocketClient","url","data","jwt","authenticatedUrl","event","channels","newChannels","channel","hook","RealtimeContext","createContext","WebSocketProvider","children","config","values","setValues","useState","hooks","setHooks","socket","useRef","token","useContext","SparkleAuthContext","useEffect","WebSocketClient","message","data","type","payload","prev","value","num","subscribe","useCallback","keys","listen","hook","React","React","createContext","useCallback","useEffect","useRef","useState","ObsContext","ObsProvider","url","children","status","setStatus","obsRef","cancelledRef","reconnectTimer","OBSWebSocket","obs","doConnect","call","requestType","requestData","createContext","useContext","SparkleConfigContext","useSparkleConfig","cfg","ClientGate","children","ready","setReady","useState","useEffect","defaultConfig","SparkleProvider","config","mergedConfig","obsUrl","useMemo","inner","React","SparkleConfigContext","AuthProvider","WebSocketProvider","ObsProvider","useContext","useEffect","useMemo","useRealtime","key","defaultValue","context","useContext","RealtimeContext","keys","useMemo","useEffect","k","useEffect","useState","WIDGET_SESSION_REQ","WIDGET_SESSION_RES","newRequestId","isEmbedded","parseSessionMessage","d","o","userId","username","profilePicture","requestSessionViaParent","target","resolve","requestId","onMsg","ev","timer","fetchSessionPayload","res","data","useAuth","setUserId","setUsername","setProfilePicture","cancelled","p","useContext","useEffect","SparkleHookKeys","useHook","hook","context","useContext","RealtimeContext","useEffect","useCallback","useMemo","BRIDGE_TYPE","BRIDGE_RESULT","newRequestId","bridgeInvoke","projectId","body","target","resolve","reject","requestId","onMsg","ev","d","timer","shouldUsePostMessageBridge","fetchInvoke","res","err","useTask","useSparkleConfig","invoke","useCallback","taskId","payload","invokeUntil","delaySeconds","useMemo","useCallback","useMemo","createTwitchGetClient","invoke","call","ns","method","args","BRIDGE_TYPE","BRIDGE_RESULT","newRequestId","shouldUsePostMessageBridge","bridgeTwitchGet","projectId","body","target","resolve","reject","requestId","onMsg","ev","d","timer","fetchTwitchGet","res","err","useTwitch","useSparkleConfig","invoke","useCallback","namespace","method","args","useMemo","createTwitchGetClient","useCallback","useContext","useEffect","useRef","useState","parseCounts","raw","record","read","key","value","parseLatest","cheers","tips","parseRecentList","item","coerceChannelLabelsSnapshot","session","total","latest","recentRaw","emptyChannelLabels","LABELS_SNAPSHOT_KEY","BRIDGE_TYPE","BRIDGE_RESULT","newRequestId","isEmbeddedFrame","bridgeLabelsGet","projectId","target","resolve","reject","requestId","onMsg","ev","d","timer","fetchLabels","res","err","emptyChannelLabels","loadLabelsFallback","useBridge","useLabels","config","useSparkleConfig","userId","realtime","useContext","RealtimeContext","useRealtimeLabels","labels","setLabels","useState","requestIdRef","useRef","realtimeSnapshot","useEffect","parsed","coerceChannelLabelsSnapshot","refresh","useCallback","next","useCallback","useEffect","useRef","useState","newRequestId","isEmbeddedFrame","bridgeSpotifyGet","projectId","target","resolve","reject","requestId","onMsg","ev","d","timer","fetchSpotify","res","err","useSpotify","options","useSparkleConfig","intervalMs","useBridge","track","setTrack","useState","loading","setLoading","error","setError","requestIdRef","useRef","refresh","useCallback","data","useEffect","useCallback","ELEVENLABS_PRESET_VOICES","newRequestId","isEmbeddedFrame","bridgeElevenLabsSpeak","userId","token","text","options","target","resolve","reject","requestId","onMsg","ev","d","timer","blob","url","audio","fetchElevenLabsSpeak","params","res","useVoices","config","useSparkleConfig","apiKey","useBridge","useCallback","preset","v","apiOptions","useCallback","useState","newRequestId","isEmbeddedFrame","bridgeAiGenerate","userId","token","prompt","model","target","resolve","reject","requestId","onMsg","ev","d","timer","fetchAiGenerate","params","res","errData","useGenerate","config","useSparkleConfig","apiKey","useBridge","isGenerating","setIsGenerating","useState","error","setError","useCallback","options","err","e"]}
1
+ {"version":3,"sources":["../src/provider.tsx","../src/providers/authProvider.tsx","../src/providers/realtimeProvider.tsx","../src/lib/websocket.ts","../src/providers/obsProvider.tsx","../src/sparkle-config-context.tsx","../src/hooks/useRealtime.ts","../src/hooks/useAuth.ts","../src/hooks/useHook.ts","../src/hooks/useTask.ts","../src/hooks/useTwitch.ts","../src/twitch/create-twitch-get-client.ts","../src/hooks/useLabels.ts","../src/types/labels.ts","../src/hooks/useSpotify.ts","../src/hooks/useVoices.ts","../src/hooks/useGenerate.ts"],"sourcesContent":["import React, { useMemo, type ReactNode, useEffect, useState } from \"react\"\nimport { AuthProvider } from \"./providers/authProvider\"\nimport { WebSocketProvider } from \"./providers/realtimeProvider\"\nimport { ObsProvider } from \"./providers/obsProvider\"\nimport { SparkleConfigContext } from \"./sparkle-config-context\"\nimport { SparkleConfig } from \"./utils/config\"\n\nconst ClientGate = ({ children }: { children: ReactNode }) => {\n const [ready, setReady] = useState(false)\n\n useEffect(() => {\n setReady(true)\n }, [])\n\n if (!ready) return null\n return children\n}\n\nexport interface SparkleProviderProps {\n config?: SparkleConfig\n children: ReactNode\n}\n\nconst defaultConfig: SparkleConfig = {\n userId: \"\",\n wsUrl: \"wss://sparkle-bot-v2.fly.dev/ws\",\n}\n\nexport const SparkleProvider = ({ children, config }: SparkleProviderProps) => {\n const mergedConfig = { ...defaultConfig, ...config }\n\n const obsUrl = useMemo(() => {\n if (!mergedConfig.obs?.clientId) return \"\"\n const base =\n mergedConfig.obs.bridgeUrl ?? \"wss://sparkle-bot-v2.fly.dev/obs-bridge\"\n return `${base}?role=client&clientId=${encodeURIComponent(mergedConfig.obs.clientId)}`\n }, [mergedConfig.obs?.clientId, mergedConfig.obs?.bridgeUrl])\n\n const inner = (\n <SparkleConfigContext.Provider value={mergedConfig}>\n <AuthProvider config={mergedConfig}>\n <WebSocketProvider config={mergedConfig}>\n <ClientGate>{children}</ClientGate>\n </WebSocketProvider>\n </AuthProvider>\n </SparkleConfigContext.Provider>\n )\n\n if (obsUrl) {\n return <ObsProvider url={obsUrl}>{inner}</ObsProvider>\n }\n\n return inner\n}\n","import * as React from \"react\"\r\n\r\nimport { type ReactNode, createContext } from \"react\"\r\n\r\nimport { SparkleConfig } from \"../utils/config\"\r\n\r\ninterface AuthContextType {\r\n token: string | undefined | null\r\n}\r\n\r\nexport const SparkleAuthContext = createContext<AuthContextType>({\r\n token: null,\r\n})\r\n\r\ninterface AuthProviderProps {\r\n config: SparkleConfig\r\n children: ReactNode\r\n}\r\n\r\nexport const AuthProvider = ({ children, config }: AuthProviderProps) => {\r\n return (\r\n <SparkleAuthContext.Provider\r\n value={{\r\n token: config.userId,\r\n }}\r\n >\r\n {children}\r\n </SparkleAuthContext.Provider>\r\n )\r\n}\r\n","import React, { ReactNode, useCallback, useContext, useRef } from \"react\"\r\n\r\nimport { createContext, useEffect, useState } from \"react\"\r\nimport { SparkleAuthContext } from \"./authProvider\"\r\nimport { UpdateMessage, WebSocketClient } from \"../lib/websocket\"\r\nimport { SparkleConfig } from \"../utils/config\"\r\n\r\ninterface WebSocketContextType {\r\n subscribe: (keys: string[]) => void\r\n listen: (hook: string) => void\r\n values: Record<string, any>\r\n hooks: Record<string, any>\r\n}\r\n\r\nexport const RealtimeContext = createContext<WebSocketContextType>({\r\n subscribe: () => {},\r\n listen: () => {},\r\n values: {},\r\n hooks: {},\r\n})\r\n\r\nexport type RealtimeValue = string | number | boolean | { [key: string]: any }\r\n\r\ntype State = Record<string, RealtimeValue>\r\n\r\ninterface WebSocketProviderProps {\r\n config: SparkleConfig\r\n children: ReactNode\r\n}\r\n\r\nexport const WebSocketProvider = ({\r\n children,\r\n config,\r\n}: WebSocketProviderProps) => {\r\n const [values, setValues] = useState<State>({})\r\n const [hooks, setHooks] = useState<Record<string, any>>({})\r\n\r\n const socket = useRef<WebSocketClient>(null)\r\n\r\n const { token } = useContext(SparkleAuthContext)\r\n\r\n useEffect(() => {\r\n socket.current = new WebSocketClient(config.wsUrl)\r\n\r\n socket.current.on(\"update\", (message: UpdateMessage) => {\r\n const { data, type } = message\r\n\r\n if (type === \"hook\") {\r\n const payload = JSON.parse(data.value)\r\n\r\n setHooks((prev) => ({\r\n ...prev,\r\n [payload.hook]: payload.payload,\r\n }))\r\n console.log(\"receive hooks\", payload)\r\n } else {\r\n let value: any = data.value\r\n\r\n console.log(\"value\", value)\r\n\r\n try {\r\n const num = parseFloat(data.value)\r\n if (!isNaN(num) && String(num) === String(data.value).trim()) {\r\n value = num\r\n } else {\r\n try {\r\n value = JSON.parse(data.value)\r\n } catch {\r\n value = data.value\r\n }\r\n }\r\n } catch {\r\n value = data.value\r\n }\r\n\r\n setValues((prev) => ({ ...prev, [data.key]: value }))\r\n console.log(\"receive values\", values)\r\n }\r\n })\r\n\r\n return () => {\r\n socket.current?.disconnect()\r\n }\r\n }, [])\r\n\r\n useEffect(() => {\r\n if (!socket.current || !token) return\r\n\r\n socket.current.authenticate(token)\r\n }, [token])\r\n\r\n const subscribe = useCallback(\r\n (keys: string[]) => {\r\n socket.current?.subscribe(keys)\r\n },\r\n [socket]\r\n )\r\n\r\n const listen = useCallback(\r\n (hook: string) => {\r\n socket.current?.hook(hook)\r\n },\r\n [socket]\r\n )\r\n\r\n return (\r\n <RealtimeContext.Provider\r\n value={{\r\n subscribe,\r\n listen,\r\n values,\r\n hooks,\r\n }}\r\n >\r\n {children}\r\n </RealtimeContext.Provider>\r\n )\r\n}\r\n","import EventEmitter from \"events\"\r\nimport z from \"zod\"\r\n\r\nconst subscribeSchema = z.object({\r\n type: z.literal(\"subscribe\"),\r\n channels: z.array(z.string()),\r\n})\r\n\r\nconst hookSchema = z.object({\r\n type: z.literal(\"hook\"),\r\n channels: z.array(z.string()),\r\n})\r\n\r\nconst unsubscribeSchema = z.object({\r\n type: z.literal(\"unsubscribe\"),\r\n channels: z.string(),\r\n})\r\n\r\nconst updateSchema = z.object({\r\n event: z.literal(\"update\"),\r\n type: z.enum([\"value\", \"hook\"]),\r\n data: z.object({\r\n key: z.string(),\r\n value: z.string(),\r\n }),\r\n})\r\n\r\nconst messageSchema = z.union([subscribeSchema, unsubscribeSchema, hookSchema])\r\n\r\nexport type Message = z.infer<typeof messageSchema>\r\n\r\nexport type UpdateMessage = z.infer<typeof updateSchema>\r\nexport type SubscribeMessage = z.infer<typeof subscribeSchema>\r\nexport type UnsubscribeMessage = z.infer<typeof unsubscribeSchema>\r\n\r\nexport class WebSocketClient extends EventEmitter {\r\n private socket: WebSocket | null = null\r\n private queue: Message[] = []\r\n private subscriptions: string[] = []\r\n private hooks: string[] = []\r\n private isAuthenticating = false\r\n\r\n private url: string\r\n private jwt: string | null = null\r\n\r\n constructor(url: string) {\r\n super()\r\n this.url = url\r\n }\r\n\r\n private send(data: Message) {\r\n if (this.socket?.readyState === WebSocket.OPEN) {\r\n this.socket?.send(JSON.stringify(data))\r\n } else {\r\n this.queue.push(data)\r\n }\r\n }\r\n\r\n authenticate(jwt: string) {\r\n if (this.isAuthenticating) return\r\n this.isAuthenticating = true\r\n this.jwt = jwt\r\n this.connect()\r\n }\r\n\r\n private connect() {\r\n const authenticatedUrl = this.url + \"?jwt=\" + this.jwt\r\n this.socket = new WebSocket(authenticatedUrl)\r\n\r\n this.socket.onopen = () => {\r\n this.queue.forEach((data) => this.send(data))\r\n this.queue = []\r\n }\r\n\r\n this.socket.onmessage = (event) => {\r\n const data = JSON.parse(event.data)\r\n this.emit(\"update\", data)\r\n }\r\n\r\n this.socket.onclose = () => {\r\n this.reconnect()\r\n }\r\n }\r\n\r\n reconnect() {\r\n setTimeout(() => {\r\n this.queue = [\r\n {\r\n type: \"subscribe\",\r\n channels: this.subscriptions,\r\n },\r\n {\r\n type: \"hook\",\r\n channels: this.hooks,\r\n },\r\n ]\r\n\r\n console.log(\"try reconnecting\")\r\n\r\n this.connect()\r\n }, 5000)\r\n }\r\n\r\n subscribe(channels: string[]) {\r\n const newChannels = channels.filter(\r\n (channel) => !this.subscriptions.includes(channel),\r\n )\r\n\r\n if (!newChannels.length) return\r\n this.subscriptions = [...this.subscriptions, ...newChannels]\r\n\r\n console.log(\"subscribe\", channels)\r\n\r\n this.send({\r\n type: \"subscribe\",\r\n channels,\r\n })\r\n }\r\n\r\n hook(hook: string) {\r\n const newChannels = [hook].filter(\r\n (channel) => !this.hooks.includes(channel),\r\n )\r\n\r\n if (!newChannels.length) return\r\n this.hooks = [...this.hooks, ...newChannels]\r\n\r\n this.send({\r\n type: \"hook\",\r\n channels: [hook],\r\n })\r\n }\r\n\r\n unsubscribe(channels: string) {\r\n this.send({ type: \"unsubscribe\", channels })\r\n }\r\n\r\n disconnect() {\r\n if (!this.socket) return\r\n\r\n this.socket.onclose = () => {}\r\n this.socket.close()\r\n }\r\n}\r\n","import React, {\r\n createContext,\r\n useCallback,\r\n useEffect,\r\n useRef,\r\n useState,\r\n type ReactNode,\r\n} from \"react\"\r\nimport type OBSWebSocket from \"obs-websocket-js\"\r\nimport type { OBSRequestTypes, OBSResponseTypes } from \"obs-websocket-js\"\r\n\r\ntype ObsStatus = \"disconnected\" | \"connecting\" | \"connected\" | \"error\"\r\n\r\ninterface ObsContextType {\r\n status: ObsStatus\r\n call: <T extends keyof OBSRequestTypes>(\r\n requestType: T,\r\n requestData?: OBSRequestTypes[T]\r\n ) => Promise<OBSResponseTypes[T]>\r\n}\r\n\r\nexport const ObsContext = createContext<ObsContextType>({\r\n status: \"disconnected\",\r\n call: () => Promise.reject(new Error(\"ObsProvider not mounted\")),\r\n})\r\n\r\nexport interface ObsProviderProps {\r\n url: string\r\n children: ReactNode\r\n}\r\n\r\nexport function ObsProvider({ url, children }: ObsProviderProps) {\r\n const [status, setStatus] = useState<ObsStatus>(\"disconnected\")\r\n const obsRef = useRef<OBSWebSocket | null>(null)\r\n const cancelledRef = useRef(false)\r\n\r\n useEffect(() => {\r\n if (!url) return\r\n\r\n cancelledRef.current = false\r\n let reconnectTimer: ReturnType<typeof setTimeout>\r\n\r\n const setup = async () => {\r\n const { default: OBSWebSocket } = await import(\"obs-websocket-js\")\r\n const obs = new OBSWebSocket()\r\n obsRef.current = obs\r\n\r\n const doConnect = async () => {\r\n if (cancelledRef.current) return\r\n setStatus(\"connecting\")\r\n try {\r\n await obs.connect(url)\r\n if (!cancelledRef.current) setStatus(\"connected\")\r\n } catch {\r\n if (!cancelledRef.current) {\r\n setStatus(\"error\")\r\n reconnectTimer = setTimeout(doConnect, 3000)\r\n }\r\n }\r\n }\r\n\r\n obs.on(\"ConnectionClosed\" as any, () => {\r\n if (!cancelledRef.current) {\r\n setStatus(\"disconnected\")\r\n reconnectTimer = setTimeout(doConnect, 3000)\r\n }\r\n })\r\n\r\n obs.on(\"ConnectionError\" as any, () => {\r\n if (!cancelledRef.current) setStatus(\"error\")\r\n })\r\n\r\n await doConnect()\r\n }\r\n\r\n setup()\r\n\r\n return () => {\r\n cancelledRef.current = true\r\n clearTimeout(reconnectTimer!)\r\n obsRef.current?.disconnect()\r\n obsRef.current = null\r\n setStatus(\"disconnected\")\r\n }\r\n }, [url])\r\n\r\n const call = useCallback(\r\n async <T extends keyof OBSRequestTypes>(\r\n requestType: T,\r\n requestData?: OBSRequestTypes[T]\r\n ): Promise<OBSResponseTypes[T]> => {\r\n const obs = obsRef.current\r\n if (!obs) throw new Error(\"OBS not connected\")\r\n return obs.call(requestType, requestData)\r\n },\r\n []\r\n )\r\n\r\n return (\r\n <ObsContext.Provider value={{ status, call }}>\r\n {children}\r\n </ObsContext.Provider>\r\n )\r\n}\r\n","import { createContext, useContext } from \"react\"\r\nimport type { SparkleConfig } from \"./utils/config\"\r\n\r\nexport const SparkleConfigContext = createContext<SparkleConfig | null>(null)\r\n\r\nexport function useSparkleConfig(): SparkleConfig {\r\n const cfg = useContext(SparkleConfigContext)\r\n if (!cfg) {\r\n throw new Error(\"Sparkle hooks require SparkleProvider\")\r\n }\r\n return cfg\r\n}\r\n","import { useContext, useEffect, useMemo } from \"react\"\r\nimport { RealtimeContext, RealtimeValue } from \"../providers/realtimeProvider\"\r\n\r\nexport function useRealtime<T extends RealtimeValue>(\r\n key: string,\r\n defaultValue?: T\r\n): T[] {\r\n const context = useContext(RealtimeContext)\r\n\r\n const keys = useMemo(() => [key], [key])\r\n\r\n if (!context) {\r\n throw new Error(\"You must use useRealtime inside a SparkleProvider\")\r\n }\r\n\r\n useEffect(() => {\r\n context.subscribe([key])\r\n }, [keys])\r\n\r\n return keys.map((k) => (context.values[k] ?? defaultValue) as T)\r\n}\r\n","import { useEffect, useState } from \"react\"\n\nconst WIDGET_SESSION_REQ = \"sparkle-widget-session-request\"\nconst WIDGET_SESSION_RES = \"sparkle-widget-session-result\"\n\nfunction newRequestId(): string {\n if (typeof crypto !== \"undefined\" && \"randomUUID\" in crypto) {\n return crypto.randomUUID()\n }\n return `${Date.now()}-${Math.random().toString(16).slice(2)}`\n}\n\nfunction isEmbedded(): boolean {\n if (typeof window === \"undefined\") return false\n try {\n return window.self !== window.top\n } catch {\n return true\n }\n}\n\nfunction parseSessionMessage(d: unknown): {\n userId: string | null\n username: string | null\n profilePicture: string | null\n} {\n if (!d || typeof d !== \"object\") {\n return { userId: null, username: null, profilePicture: null }\n }\n const o = d as Record<string, unknown>\n const userId =\n typeof o.userId === \"string\" && o.userId.trim() ? o.userId : null\n const username =\n typeof o.username === \"string\" && o.username.trim() ? o.username : null\n const profilePicture =\n typeof o.profilePicture === \"string\" && o.profilePicture.trim()\n ? o.profilePicture\n : null\n return { userId, username, profilePicture }\n}\n\nfunction requestSessionViaParent(): Promise<{\n userId: string | null\n username: string | null\n profilePicture: string | null\n}> {\n if (typeof window === \"undefined\") {\n return Promise.resolve({\n userId: null,\n username: null,\n profilePicture: null,\n })\n }\n const target = window.top ?? window.parent\n if (!target || target === window) {\n return Promise.resolve({\n userId: null,\n username: null,\n profilePicture: null,\n })\n }\n return new Promise((resolve) => {\n const requestId = newRequestId()\n const onMsg = (ev: MessageEvent) => {\n const d = ev.data\n if (!d || d.type !== WIDGET_SESSION_RES || d.requestId !== requestId)\n return\n window.removeEventListener(\"message\", onMsg)\n window.clearTimeout(timer)\n resolve(parseSessionMessage(d))\n }\n const timer = window.setTimeout(() => {\n window.removeEventListener(\"message\", onMsg)\n resolve({ userId: null, username: null, profilePicture: null })\n }, 12_000)\n window.addEventListener(\"message\", onMsg)\n target.postMessage({ type: WIDGET_SESSION_REQ, requestId }, \"*\")\n })\n}\n\nasync function fetchSessionPayload(): Promise<{\n userId: string | null\n username: string | null\n profilePicture: string | null\n}> {\n const res = await fetch(\"/api/sparkle-widget-session\", {\n method: \"GET\",\n credentials: \"include\",\n })\n if (!res.ok) {\n return { userId: null, username: null, profilePicture: null }\n }\n const data = await res.json().catch(() => null)\n return parseSessionMessage(data)\n}\n\nexport type UseAuthResult = {\n /** Identifiant Twitch (`Account.accountId`), pas l’id interne Sparkle. */\n userId: string | null | undefined\n /** `User.name` Prisma (nom affiché OAuth). */\n username: string | null | undefined\n /** `User.image` Prisma (URL avatar). */\n profilePicture: string | null | undefined\n}\n\n/**\n * Profil session navigateur : `userId` = id Twitch (`Account.accountId` OAuth) ;\n * `username` / `profilePicture` depuis `User`. Champs à `undefined` pendant le chargement.\n */\nexport function useAuth(): UseAuthResult {\n const [userId, setUserId] = useState<string | null | undefined>(undefined)\n const [username, setUsername] = useState<string | null | undefined>(undefined)\n const [profilePicture, setProfilePicture] = useState<\n string | null | undefined\n >(undefined)\n\n useEffect(() => {\n let cancelled = false\n const run = async () => {\n const p = isEmbedded()\n ? await requestSessionViaParent()\n : await fetchSessionPayload()\n if (!cancelled) {\n setUserId(p.userId)\n setUsername(p.username)\n setProfilePicture(p.profilePicture)\n }\n }\n void run()\n return () => {\n cancelled = true\n }\n }, [])\n\n return { userId, username, profilePicture }\n}\n","import { useContext, useEffect, useMemo } from \"react\"\r\nimport { RealtimeContext } from \"../providers/realtimeProvider\"\r\nimport { EventTypeMap } from \"./hook\"\r\n\r\nexport const SparkleHookKeys = [\r\n \"notification.follow\",\r\n \"notification.cheer\",\r\n \"notification.tips\",\r\n \"notification.subscribe\",\r\n \"notification.subscribe.end\",\r\n \"notification.subscribe.gift\",\r\n \"notification.subscription.message\",\r\n \"notification.raid\",\r\n\r\n \"chat.message\",\r\n\r\n \"stream.online\",\r\n \"stream.offline\",\r\n \"stream.update\",\r\n\r\n \"reward.create\",\r\n \"reward.update\",\r\n \"reward.remove\",\r\n\r\n \"reward.redemption.claim\",\r\n \"reward.redemption.update\",\r\n\r\n \"poll.begin\",\r\n \"poll.progress\",\r\n \"poll.end\",\r\n\r\n \"prediction.begin\",\r\n \"prediction.progress\",\r\n \"prediction.lock\",\r\n \"prediction.end\",\r\n\r\n \"moderator.add\",\r\n \"moderator.remove\",\r\n \"moderator.ban\",\r\n \"moderator.unban\",\r\n \"moderator.shield_mode.begin\",\r\n] as const\r\n\r\nexport type SparkleHookKey = (typeof SparkleHookKeys)[number]\r\n\r\nexport function useHook<T extends keyof EventTypeMap>(\r\n hook: T\r\n): EventTypeMap[T] | undefined {\r\n const context = useContext(RealtimeContext)\r\n\r\n if (!context) {\r\n throw new Error(\"You must use useHook inside a SparkleProvider\")\r\n }\r\n\r\n useEffect(() => {\r\n context.listen(hook)\r\n }, [hook])\r\n\r\n return context.hooks[hook] as EventTypeMap[T]\r\n}\r\n","import { useCallback, useMemo } from \"react\"\r\nimport { useSparkleConfig } from \"../sparkle-config-context\"\r\n\r\nconst BRIDGE_TYPE = \"sparkle-invoke-task\"\r\nconst BRIDGE_RESULT = \"sparkle-invoke-task-result\"\r\n\r\ntype InvokeBridgeBody =\r\n | { kind: \"invoke\"; taskId: string; payload?: unknown }\r\n | {\r\n kind: \"invokeUntil\"\r\n taskId: string\r\n payload?: unknown\r\n delaySeconds: number\r\n }\r\n\r\nfunction newRequestId(): string {\r\n if (typeof crypto !== \"undefined\" && \"randomUUID\" in crypto) {\r\n return crypto.randomUUID()\r\n }\r\n return `${Date.now()}-${Math.random().toString(16).slice(2)}`\r\n}\r\n\r\nfunction bridgeInvoke(\r\n projectId: string,\r\n body: InvokeBridgeBody\r\n): Promise<boolean> {\r\n if (typeof window === \"undefined\") {\r\n return Promise.reject(new Error(\"useTask requires a browser environment\"))\r\n }\r\n const target = window.top ?? window.parent\r\n if (!target || target === window) {\r\n return Promise.reject(\r\n new Error(\r\n \"useTask: aucun parent — utilisez la preview éditeur ou une page intégrée.\"\r\n )\r\n )\r\n }\r\n\r\n return new Promise((resolve, reject) => {\r\n const requestId = newRequestId()\r\n const onMsg = (ev: MessageEvent) => {\r\n const d = ev.data\r\n if (!d || d.type !== BRIDGE_RESULT || d.requestId !== requestId) return\r\n window.removeEventListener(\"message\", onMsg)\r\n window.clearTimeout(timer)\r\n if (d.ok) resolve(Boolean(d.result))\r\n else reject(new Error(typeof d.error === \"string\" ? d.error : \"invoke_failed\"))\r\n }\r\n const timer = window.setTimeout(() => {\r\n window.removeEventListener(\"message\", onMsg)\r\n reject(new Error(\"useTask: délai dépassé\"))\r\n }, 45_000)\r\n window.addEventListener(\"message\", onMsg)\r\n target.postMessage({ type: BRIDGE_TYPE, requestId, projectId, ...body }, \"*\")\r\n })\r\n}\r\n\r\n/**\r\n * Dès que le widget tourne dans une iframe, un `fetch` vers `/api/...` n’emporte en général\r\n * pas la session (partitionnement / contexte imbriqué). Le relais `postMessage` → fenêtre\r\n * parente (même onglet, même origine) envoie les cookies comme une navigation classique.\r\n */\r\nfunction shouldUsePostMessageBridge(): boolean {\r\n if (typeof window === \"undefined\") return false\r\n try {\r\n return window.self !== window.top\r\n } catch {\r\n return true\r\n }\r\n}\r\n\r\nasync function fetchInvoke(\r\n projectId: string,\r\n body: InvokeBridgeBody\r\n): Promise<boolean> {\r\n const res = await fetch(`/api/projects/${projectId}/invoke-task`, {\r\n method: \"POST\",\r\n credentials: \"include\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify(body),\r\n })\r\n if (!res.ok) {\r\n const err = (await res.json().catch(() => null)) as { error?: string } | null\r\n throw new Error(err?.error ?? res.statusText)\r\n }\r\n return true\r\n}\r\n\r\n/**\r\n * Exécute une tâche Sparkle dans la runtime (équivalent à `ctx.invoke` / `ctx.invokeUntil`).\r\n * En preview Sandpack, les appels passent par `postMessage` vers l’éditeur (session).\r\n */\r\nexport function useTask() {\r\n const config = useSparkleConfig()\r\n const projectId = config.projectId\r\n\r\n const invoke = useCallback(\r\n async (taskId: string, payload?: unknown) => {\r\n if (!projectId?.trim()) {\r\n throw new Error(\"useTask: renseignez `config.projectId` sur SparkleProvider.\")\r\n }\r\n const body: InvokeBridgeBody = { kind: \"invoke\", taskId, payload }\r\n if (shouldUsePostMessageBridge()) {\r\n return bridgeInvoke(projectId, body)\r\n }\r\n return fetchInvoke(projectId, body)\r\n },\r\n [projectId]\r\n )\r\n\r\n const invokeUntil = useCallback(\r\n async (taskId: string, delaySeconds: number, payload?: unknown) => {\r\n if (!projectId?.trim()) {\r\n throw new Error(\"useTask: renseignez `config.projectId` sur SparkleProvider.\")\r\n }\r\n const body: InvokeBridgeBody = {\r\n kind: \"invokeUntil\",\r\n taskId,\r\n payload,\r\n delaySeconds,\r\n }\r\n if (shouldUsePostMessageBridge()) {\r\n return bridgeInvoke(projectId, body)\r\n }\r\n return fetchInvoke(projectId, body)\r\n },\r\n [projectId]\r\n )\r\n\r\n return useMemo(() => ({ invoke, invokeUntil }), [invoke, invokeUntil])\r\n}\r\n","import { useCallback, useMemo } from \"react\"\r\nimport { useSparkleConfig } from \"../sparkle-config-context\"\r\nimport { createTwitchGetClient } from \"../twitch/create-twitch-get-client\"\r\nimport type { TwitchGetClient } from \"../twitch/twitch-get-types\"\r\n\r\nexport type { TwitchGetClient } from \"../twitch/twitch-get-types\"\r\n\r\nconst BRIDGE_TYPE = \"sparkle-twitch-get\"\r\nconst BRIDGE_RESULT = \"sparkle-twitch-get-result\"\r\n\r\nexport type TwitchGetApiBody = {\r\n path: string[]\r\n args?: unknown[]\r\n}\r\n\r\nfunction newRequestId(): string {\r\n if (typeof crypto !== \"undefined\" && \"randomUUID\" in crypto) {\r\n return crypto.randomUUID()\r\n }\r\n return `${Date.now()}-${Math.random().toString(16).slice(2)}`\r\n}\r\n\r\nfunction shouldUsePostMessageBridge(): boolean {\r\n if (typeof window === \"undefined\") return false\r\n try {\r\n return window.self !== window.top\r\n } catch {\r\n return true\r\n }\r\n}\r\n\r\nfunction bridgeTwitchGet(\r\n projectId: string,\r\n body: TwitchGetApiBody\r\n): Promise<unknown> {\r\n if (typeof window === \"undefined\") {\r\n return Promise.reject(new Error(\"useTwitch requires a browser environment\"))\r\n }\r\n const target = window.top ?? window.parent\r\n if (!target || target === window) {\r\n return Promise.reject(\r\n new Error(\r\n \"useTwitch: aucun parent — utilisez la preview éditeur ou une page intégrée.\"\r\n )\r\n )\r\n }\r\n\r\n return new Promise((resolve, reject) => {\r\n const requestId = newRequestId()\r\n const onMsg = (ev: MessageEvent) => {\r\n const d = ev.data\r\n if (!d || d.type !== BRIDGE_RESULT || d.requestId !== requestId) return\r\n window.removeEventListener(\"message\", onMsg)\r\n window.clearTimeout(timer)\r\n if (d.ok) resolve(d.result)\r\n else\r\n reject(\r\n new Error(typeof d.error === \"string\" ? d.error : \"twitch_get_failed\")\r\n )\r\n }\r\n const timer = window.setTimeout(() => {\r\n window.removeEventListener(\"message\", onMsg)\r\n reject(new Error(\"useTwitch: délai dépassé\"))\r\n }, 45_000)\r\n window.addEventListener(\"message\", onMsg)\r\n target.postMessage(\r\n { type: BRIDGE_TYPE, requestId, projectId, ...body },\r\n \"*\"\r\n )\r\n })\r\n}\r\n\r\nasync function fetchTwitchGet(\r\n projectId: string,\r\n body: TwitchGetApiBody\r\n): Promise<unknown> {\r\n const res = await fetch(\r\n `/api/projects/${encodeURIComponent(projectId)}/twitch-get`,\r\n {\r\n method: \"POST\",\r\n credentials: \"include\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify(body),\r\n }\r\n )\r\n if (!res.ok) {\r\n const err = (await res.json().catch(() => null)) as { error?: string } | null\r\n throw new Error(err?.error ?? res.statusText)\r\n }\r\n const json = (await res.json()) as { result?: unknown }\r\n return json.result\r\n}\r\n\r\n/**\r\n * Client Twitch en lecture seule (Twurple via le backend du projet).\r\n * Même surface GET que `ctx.twitch` dans les handlers runtime — pas les mutations.\r\n *\r\n * Requiert `config.projectId` sur `SparkleProvider`.\r\n */\r\nexport function useTwitch(): TwitchGetClient {\r\n const config = useSparkleConfig()\r\n const projectId = config.projectId\r\n\r\n const invoke = useCallback(\r\n async (namespace: string, method: string, args: unknown[]) => {\r\n if (!projectId?.trim()) {\r\n throw new Error(\r\n \"useTwitch: renseignez `config.projectId` sur SparkleProvider.\"\r\n )\r\n }\r\n const body: TwitchGetApiBody = {\r\n path: [namespace, method],\r\n args,\r\n }\r\n if (shouldUsePostMessageBridge()) {\r\n return bridgeTwitchGet(projectId, body)\r\n }\r\n return fetchTwitchGet(projectId, body)\r\n },\r\n [projectId]\r\n )\r\n\r\n return useMemo(() => createTwitchGetClient(invoke), [invoke])\r\n}\r\n","import type { TwitchGetClient } from \"./twitch-get-types\"\n\nexport type TwitchGetInvoker = (\n namespace: string,\n method: string,\n args: unknown[]\n) => Promise<unknown>\n\nexport function createTwitchGetClient(\n invoke: TwitchGetInvoker\n): TwitchGetClient {\n const call =\n (ns: string, method: string) =>\n (...args: unknown[]) =>\n invoke(ns, method, args)\n\n return {\n bits: {\n getLeaderboard: call(\"bits\", \"getLeaderboard\"),\n },\n channelPoints: {\n getCustomRewards: call(\"channelPoints\", \"getCustomRewards\"),\n getCustomRewardsByIds: call(\"channelPoints\", \"getCustomRewardsByIds\"),\n getCustomRewardById: call(\"channelPoints\", \"getCustomRewardById\"),\n getRedemptionsByIds: call(\"channelPoints\", \"getRedemptionsByIds\"),\n getRedemptionById: call(\"channelPoints\", \"getRedemptionById\"),\n getRedemptions: call(\"channelPoints\", \"getRedemptions\"),\n },\n channel: {\n getChannelInfoById: call(\"channel\", \"getChannelInfoById\"),\n getFollowDate: call(\"channel\", \"getFollowDate\"),\n getChannelEditors: call(\"channel\", \"getChannelEditors\"),\n getVips: call(\"channel\", \"getVips\"),\n checkVipForUsers: call(\"channel\", \"checkVipForUsers\"),\n checkVipForUser: call(\"channel\", \"checkVipForUser\"),\n getChannelFollowerCount: call(\"channel\", \"getChannelFollowerCount\"),\n },\n chat: {\n getChatters: call(\"chat\", \"getChatters\"),\n getSettings: call(\"chat\", \"getSettings\"),\n getSettingsPrivileged: call(\"chat\", \"getSettingsPrivileged\"),\n getColorsForUsers: call(\"chat\", \"getColorsForUsers\"),\n getColorForUser: call(\"chat\", \"getColorForUser\"),\n },\n clips: {\n getClips: call(\"clips\", \"getClips\"),\n getClipById: call(\"clips\", \"getClipById\"),\n getClipsByIds: call(\"clips\", \"getClipsByIds\"),\n },\n goals: {\n getGoals: call(\"goals\", \"getGoals\"),\n },\n polls: {\n getPolls: call(\"polls\", \"getPolls\"),\n getPollsByIds: call(\"polls\", \"getPollsByIds\"),\n getPollById: call(\"polls\", \"getPollById\"),\n },\n predictions: {\n getPredictions: call(\"predictions\", \"getPredictions\"),\n getPredictionsByIds: call(\"predictions\", \"getPredictionsByIds\"),\n getPredictionById: call(\"predictions\", \"getPredictionById\"),\n },\n streams: {\n getStream: call(\"streams\", \"getStream\"),\n getStreamMarkers: call(\"streams\", \"getStreamMarkers\"),\n },\n subscriptions: {\n getSubscriptions: call(\"subscriptions\", \"getSubscriptions\"),\n getSubscriptionsForUsers: call(\n \"subscriptions\",\n \"getSubscriptionsForUsers\"\n ),\n getSubscriptionForUser: call(\"subscriptions\", \"getSubscriptionForUser\"),\n checkUserSubscription: call(\"subscriptions\", \"checkUserSubscription\"),\n },\n moderation: {\n getBanned: call(\"moderation\", \"getBanned\"),\n checkBan: call(\"moderation\", \"checkBan\"),\n getModerators: call(\"moderation\", \"getModerators\"),\n checkModerator: call(\"moderation\", \"checkModerator\"),\n getBlockedTerms: call(\"moderation\", \"getBlockedTerms\"),\n },\n utils: {\n getUserById: call(\"utils\", \"getUserById\"),\n getUserByName: call(\"utils\", \"getUserByName\"),\n getUserByIds: call(\"utils\", \"getUserByIds\"),\n getUserByNames: call(\"utils\", \"getUserByNames\"),\n },\n } as unknown as TwitchGetClient\n}\n","import { useCallback, useContext, useEffect, useRef, useState } from \"react\"\r\n\r\nimport { RealtimeContext } from \"../providers/realtimeProvider\"\r\nimport { useSparkleConfig } from \"../sparkle-config-context\"\r\nimport {\r\n coerceChannelLabelsSnapshot,\r\n emptyChannelLabels,\r\n type ChannelLabelsSnapshot,\r\n} from \"../types/labels\"\r\n\r\nconst LABELS_SNAPSHOT_KEY = \"labels:snapshot\"\r\nconst BRIDGE_TYPE = \"sparkle-labels-get\"\r\nconst BRIDGE_RESULT = \"sparkle-labels-get-result\"\r\n\r\nfunction newRequestId(): string {\r\n if (typeof crypto !== \"undefined\" && \"randomUUID\" in crypto) {\r\n return crypto.randomUUID()\r\n }\r\n return `${Date.now()}-${Math.random().toString(16).slice(2)}`\r\n}\r\n\r\nfunction isEmbeddedFrame(): boolean {\r\n if (typeof window === \"undefined\") return false\r\n try {\r\n return window.self !== window.top\r\n } catch {\r\n return true\r\n }\r\n}\r\n\r\nfunction bridgeLabelsGet(projectId: string): Promise<ChannelLabelsSnapshot> {\r\n if (typeof window === \"undefined\") {\r\n return Promise.reject(new Error(\"useLabels requires a browser environment\"))\r\n }\r\n const target = window.top ?? window.parent\r\n if (!target || target === window) {\r\n return Promise.reject(\r\n new Error(\r\n \"useLabels: aucun parent — utilisez la preview éditeur ou une page intégrée.\"\r\n )\r\n )\r\n }\r\n\r\n return new Promise((resolve, reject) => {\r\n const requestId = newRequestId()\r\n const onMsg = (ev: MessageEvent) => {\r\n const d = ev.data\r\n if (!d || d.type !== BRIDGE_RESULT || d.requestId !== requestId) return\r\n window.removeEventListener(\"message\", onMsg)\r\n window.clearTimeout(timer)\r\n if (d.ok && d.labels) resolve(d.labels as ChannelLabelsSnapshot)\r\n else\r\n reject(\r\n new Error(typeof d.error === \"string\" ? d.error : \"labels_get_failed\")\r\n )\r\n }\r\n const timer = window.setTimeout(() => {\r\n window.removeEventListener(\"message\", onMsg)\r\n reject(new Error(\"useLabels: délai dépassé\"))\r\n }, 45_000)\r\n window.addEventListener(\"message\", onMsg)\r\n target.postMessage({ type: BRIDGE_TYPE, requestId, projectId }, \"*\")\r\n })\r\n}\r\n\r\nasync function fetchLabels(projectId: string): Promise<ChannelLabelsSnapshot> {\r\n const res = await fetch(\r\n `/api/projects/${encodeURIComponent(projectId)}/labels`,\r\n {\r\n method: \"GET\",\r\n credentials: \"include\",\r\n }\r\n )\r\n if (!res.ok) {\r\n const err = (await res.json().catch(() => null)) as { error?: string } | null\r\n throw new Error(err?.error ?? res.statusText)\r\n }\r\n const json = (await res.json()) as { labels?: ChannelLabelsSnapshot }\r\n return json.labels ?? emptyChannelLabels()\r\n}\r\n\r\nasync function loadLabelsFallback(\r\n projectId: string,\r\n useBridge: boolean\r\n): Promise<ChannelLabelsSnapshot> {\r\n if (useBridge) return bridgeLabelsGet(projectId)\r\n return fetchLabels(projectId)\r\n}\r\n\r\n/**\r\n * Agrégats chaîne (session, total, latest, recent) — même données que `ctx.labels.get()`.\r\n * Poussés en temps réel via WebSocket (`labels:snapshot`, comme `useRealtime`).\r\n * Requiert `config.projectId` et `config.userId` sur `SparkleProvider`.\r\n */\r\nexport function useLabels(): ChannelLabelsSnapshot {\r\n const config = useSparkleConfig()\r\n const projectId = config.projectId?.trim() ?? \"\"\r\n const userId = config.userId?.trim() ?? \"\"\r\n const realtime = useContext(RealtimeContext)\r\n const useRealtimeLabels = Boolean(userId)\r\n const useBridge = isEmbeddedFrame() && !useRealtimeLabels\r\n\r\n const [labels, setLabels] = useState<ChannelLabelsSnapshot>(emptyChannelLabels)\r\n const requestIdRef = useRef(0)\r\n const realtimeSnapshot = realtime.values[LABELS_SNAPSHOT_KEY]\r\n\r\n useEffect(() => {\r\n if (!useRealtimeLabels) return\r\n realtime.subscribe([LABELS_SNAPSHOT_KEY])\r\n }, [useRealtimeLabels, realtime])\r\n\r\n useEffect(() => {\r\n if (!useRealtimeLabels) return\r\n const parsed = coerceChannelLabelsSnapshot(realtimeSnapshot)\r\n if (parsed) setLabels(parsed)\r\n }, [useRealtimeLabels, realtimeSnapshot])\r\n\r\n const refresh = useCallback(async () => {\r\n if (!projectId) return\r\n const requestId = ++requestIdRef.current\r\n try {\r\n const next = await loadLabelsFallback(projectId, useBridge)\r\n if (requestId === requestIdRef.current) {\r\n setLabels(next)\r\n }\r\n } catch {\r\n // Conserve la dernière valeur connue.\r\n }\r\n }, [projectId, useBridge])\r\n\r\n useEffect(() => {\r\n if (!projectId) {\r\n setLabels(emptyChannelLabels())\r\n return\r\n }\r\n\r\n if (useRealtimeLabels) {\r\n const parsed = coerceChannelLabelsSnapshot(realtimeSnapshot)\r\n if (!parsed) void refresh()\r\n return\r\n }\r\n\r\n void refresh()\r\n }, [projectId, refresh, useRealtimeLabels, realtimeSnapshot])\r\n\r\n return labels\r\n}\r\n","export type ChannelLabelCounts = {\r\n followers: number\r\n subscribers: number\r\n cheers: number\r\n tips: number\r\n}\r\n\r\nexport type ChannelLabelLatest = {\r\n followers: string\r\n subscribers: string\r\n cheers: { amount: number; user: string }\r\n tips: { amount: number; user: string }\r\n}\r\n\r\nexport type ChannelLabelRecent = {\r\n followers: string[]\r\n subscribers: string[]\r\n cheers: string[]\r\n tips: string[]\r\n}\r\n\r\nexport type ChannelLabelsSnapshot = {\r\n session: ChannelLabelCounts\r\n total: ChannelLabelCounts\r\n latest: ChannelLabelLatest\r\n recent: ChannelLabelRecent\r\n}\r\n\r\nfunction parseCounts(raw: unknown): ChannelLabelsSnapshot[\"session\"] | null {\r\n if (!raw || typeof raw !== \"object\") return null\r\n const record = raw as Record<string, unknown>\r\n const read = (key: keyof ChannelLabelsSnapshot[\"session\"]) => {\r\n const value = Number(record[key])\r\n return Number.isFinite(value) ? Math.max(0, Math.floor(value)) : 0\r\n }\r\n return {\r\n followers: read(\"followers\"),\r\n subscribers: read(\"subscribers\"),\r\n cheers: read(\"cheers\"),\r\n tips: read(\"tips\"),\r\n }\r\n}\r\n\r\nfunction parseLatest(raw: unknown): ChannelLabelsSnapshot[\"latest\"] | null {\r\n if (!raw || typeof raw !== \"object\") return null\r\n const record = raw as Record<string, unknown>\r\n const cheers =\r\n record.cheers && typeof record.cheers === \"object\"\r\n ? (record.cheers as Record<string, unknown>)\r\n : null\r\n const tips =\r\n record.tips && typeof record.tips === \"object\"\r\n ? (record.tips as Record<string, unknown>)\r\n : null\r\n\r\n return {\r\n followers: typeof record.followers === \"string\" ? record.followers : \"\",\r\n subscribers:\r\n typeof record.subscribers === \"string\" ? record.subscribers : \"\",\r\n cheers: {\r\n amount:\r\n cheers && Number.isFinite(Number(cheers.amount))\r\n ? Math.max(0, Math.floor(Number(cheers.amount)))\r\n : 0,\r\n user: cheers && typeof cheers.user === \"string\" ? cheers.user : \"\",\r\n },\r\n tips: {\r\n amount:\r\n tips && Number.isFinite(Number(tips.amount)) ? Number(tips.amount) : 0,\r\n user: tips && typeof tips.user === \"string\" ? tips.user : \"\",\r\n },\r\n }\r\n}\r\n\r\nfunction parseRecentList(raw: unknown): string[] {\r\n if (!Array.isArray(raw)) return []\r\n return raw.filter((item): item is string => typeof item === \"string\")\r\n}\r\n\r\nexport function coerceChannelLabelsSnapshot(\r\n raw: unknown\r\n): ChannelLabelsSnapshot | null {\r\n if (!raw || typeof raw !== \"object\") return null\r\n\r\n const record = raw as Record<string, unknown>\r\n const session = parseCounts(record.session)\r\n const total = parseCounts(record.total)\r\n const latest = parseLatest(record.latest)\r\n const recentRaw =\r\n record.recent && typeof record.recent === \"object\"\r\n ? (record.recent as Record<string, unknown>)\r\n : null\r\n\r\n if (!session || !total || !latest || !recentRaw) return null\r\n\r\n return {\r\n session,\r\n total,\r\n latest,\r\n recent: {\r\n followers: parseRecentList(recentRaw.followers),\r\n subscribers: parseRecentList(recentRaw.subscribers),\r\n cheers: parseRecentList(recentRaw.cheers),\r\n tips: parseRecentList(recentRaw.tips),\r\n },\r\n }\r\n}\r\n\r\nexport const emptyChannelLabels = (): ChannelLabelsSnapshot => ({\r\n session: {\r\n followers: 0,\r\n subscribers: 0,\r\n cheers: 0,\r\n tips: 0,\r\n },\r\n total: {\r\n followers: 0,\r\n subscribers: 0,\r\n cheers: 0,\r\n tips: 0,\r\n },\r\n latest: {\r\n followers: \"\",\r\n subscribers: \"\",\r\n cheers: { amount: 0, user: \"\" },\r\n tips: { amount: 0, user: \"\" },\r\n },\r\n recent: {\r\n followers: [],\r\n subscribers: [],\r\n cheers: [],\r\n tips: [],\r\n },\r\n})\r\n","import { useCallback, useEffect, useRef, useState } from \"react\"\nimport { useSparkleConfig } from \"../sparkle-config-context\"\n\nexport interface SpotifyTrack {\n isPlaying: boolean\n title: string | null\n artist: string | null\n album: string | null\n coverUrl: string | null\n durationMs: number\n progressMs: number\n}\n\nexport interface UseSpotifyResult {\n track: SpotifyTrack | null\n loading: boolean\n error: Error | null\n refresh: () => Promise<void>\n}\n\nfunction newRequestId(): string {\n if (typeof crypto !== \"undefined\" && \"randomUUID\" in crypto) {\n return crypto.randomUUID()\n }\n return `${Date.now()}-${Math.random().toString(16).slice(2)}`\n}\n\nfunction isEmbeddedFrame(): boolean {\n if (typeof window === \"undefined\") return false\n try {\n return window.self !== window.top\n } catch {\n return true\n }\n}\n\nfunction bridgeSpotifyGet(projectId: string): Promise<SpotifyTrack | null> {\n if (typeof window === \"undefined\") {\n return Promise.reject(new Error(\"useSpotify requires a browser environment\"))\n }\n const target = window.top ?? window.parent\n if (!target || target === window) {\n return Promise.reject(\n new Error(\n \"useSpotify: aucun parent — utilisez la preview éditeur ou une page intégrée.\"\n )\n )\n }\n\n return new Promise((resolve, reject) => {\n const requestId = newRequestId()\n const onMsg = (ev: MessageEvent) => {\n const d = ev.data\n if (!d || d.type !== \"sparkle-spotify-get-result\" || d.requestId !== requestId) return\n window.removeEventListener(\"message\", onMsg)\n window.clearTimeout(timer)\n if (d.ok) resolve(d.track as SpotifyTrack | null)\n else\n reject(\n new Error(typeof d.error === \"string\" ? d.error : \"spotify_get_failed\")\n )\n }\n const timer = window.setTimeout(() => {\n window.removeEventListener(\"message\", onMsg)\n reject(new Error(\"useSpotify: délai dépassé\"))\n }, 45_000)\n window.addEventListener(\"message\", onMsg)\n target.postMessage({ type: \"sparkle-spotify-get\", requestId, projectId }, \"*\")\n })\n}\n\nasync function fetchSpotify(projectId: string): Promise<SpotifyTrack | null> {\n const res = await fetch(\n `/api/projects/${encodeURIComponent(projectId)}/spotify`,\n {\n method: \"GET\",\n credentials: \"include\",\n }\n )\n if (!res.ok) {\n const err = (await res.json().catch(() => null)) as { error?: string } | null\n throw new Error(err?.error ?? res.statusText)\n }\n const json = (await res.json()) as { track?: SpotifyTrack | null }\n return json.track ?? null\n}\n\nexport function useSpotify(options?: { intervalMs?: number }): UseSpotifyResult {\n const config = useSparkleConfig()\n const projectId = config.projectId?.trim() ?? \"\"\n const intervalMs = options?.intervalMs ?? 5000\n const useBridge = isEmbeddedFrame()\n\n const [track, setTrack] = useState<SpotifyTrack | null>(null)\n const [loading, setLoading] = useState(true)\n const [error, setError] = useState<Error | null>(null)\n\n const requestIdRef = useRef(0)\n\n const refresh = useCallback(async () => {\n if (!projectId) {\n setTrack(null)\n setLoading(false)\n return\n }\n\n const requestId = ++requestIdRef.current\n try {\n const data = useBridge\n ? await bridgeSpotifyGet(projectId)\n : await fetchSpotify(projectId)\n\n if (requestId === requestIdRef.current) {\n setTrack(data)\n setError(null)\n }\n } catch (err) {\n if (requestId === requestIdRef.current) {\n setError(err instanceof Error ? err : new Error(String(err)))\n }\n } finally {\n if (requestId === requestIdRef.current) {\n setLoading(false)\n }\n }\n }, [projectId, useBridge])\n\n useEffect(() => {\n void refresh()\n }, [refresh])\n\n useEffect(() => {\n if (intervalMs <= 0 || !projectId) return\n\n const timer = setInterval(() => {\n void refresh()\n }, intervalMs)\n\n return () => clearInterval(timer)\n }, [refresh, intervalMs, projectId])\n\n return { track, loading, error, refresh }\n}\n","import { useCallback } from \"react\"\nimport { useSparkleConfig } from \"../sparkle-config-context\"\n\nexport type ElevenLabsVoiceSettings = {\n stability?: number\n similarity_boost?: number\n style?: number\n use_speaker_boost?: boolean\n}\n\nexport type ElevenLabsTtsOptions = {\n voiceId: string\n modelId?: string\n voice_settings?: ElevenLabsVoiceSettings\n}\n\nexport const ELEVENLABS_PRESET_VOICES = [\n { id: \"adam\", voiceId: \"pNInz6obpgDQGcFmaJgB\", name: \"Adam - Dominant, Firm\" },\n { id: \"alice\", voiceId: \"Xb7hH8MSUJpSbSDYk0k2\", name: \"Alice - Clear, Engaging Educator\" },\n { id: \"andy-lin\", voiceId: \"h1i3CVVBUuF6s46cxUGG\", name: \"Andy Lin - Gentle and Friendly Narrator\" },\n { id: \"anika\", voiceId: \"ecp3DWciuUyW7BYM7II1\", name: \"Anika - Sweet and Lively\" },\n { id: \"bella\", voiceId: \"hpp4J3VqNfWAUOO0d1Us\", name: \"Bella - Professional, Bright, Warm\" },\n { id: \"bill\", voiceId: \"pqHfZKP75CvOlQylNhV4\", name: \"Bill - Wise, Mature, Balanced\" },\n { id: \"brian\", voiceId: \"nPczCjzI2devNBz1zQrb\", name: \"Brian - Deep, Resonant and Comforting\" },\n { id: \"callum\", voiceId: \"N2lVS1w4EtoT3dr4eOWO\", name: \"Callum - Husky Trickster\" },\n { id: \"charlie\", voiceId: \"IKne3meq5aSn9XLyUdCD\", name: \"Charlie - Deep, Confident, Energetic\" },\n { id: \"chris\", voiceId: \"iP95p4xoKVk53GoZ742B\", name: \"Chris - Charming, Down-to-Earth\" },\n { id: \"daniel\", voiceId: \"onwK4e9ZLuTAKqWW03F9\", name: \"Daniel - Steady Broadcaster\" },\n { id: \"eric\", voiceId: \"cjVigY5qzO86Huf0OWal\", name: \"Eric - Smooth, Trustworthy\" },\n { id: \"george\", voiceId: \"JBFqnCBsd6RMkjVDRZzb\", name: \"George - Warm, Captivating Storyteller\" },\n { id: \"harry\", voiceId: \"SOYHLrjzK2X1ezoPC6cr\", name: \"Harry - Fierce Warrior\" },\n { id: \"jessica\", voiceId: \"r1KmysJdVYZjJCm4mL3b\", name: \"Jessica - Playful, Bright, Warm\" },\n { id: \"keshavi\", voiceId: \"Ek86tj0PS0XTYchY9Ody\", name: \"Keshavi - Clear & Human AI Assistant\" },\n { id: \"laura\", voiceId: \"FGY2WhTYpPnrIDTdsKH5\", name: \"Laura - Enthusiast, Quirky Attitude\" },\n { id: \"liam\", voiceId: \"TX3LPaxmHKxFdv7VOQHJ\", name: \"Liam - Energetic, Social Media Creator\" },\n { id: \"lily\", voiceId: \"pFZP5JQG7iQjIQuC4Bku\", name: \"Lily - Velvety Actress\" },\n { id: \"matilda\", voiceId: \"XrExE9yKIg1WjnnlVkGX\", name: \"Matilda - Knowledgable, Professional\" },\n { id: \"river\", voiceId: \"SAz9YHcvj6GT2YYXdXww\", name: \"River - Relaxed, Neutral, Informative\" },\n { id: \"roger\", voiceId: \"CwhRBWXzGAHq8TQ4Fs17\", name: \"Roger - Laid-Back, Casual, Resonant\" },\n { id: \"sarah\", voiceId: \"EXAVITQu4vr4xnSDxMaL\", name: \"Sarah - Mature, Reassuring, Confident\" },\n { id: \"will\", voiceId: \"bIHbv24MWmeRgasZH58o\", name: \"Will - Relaxed Optimist\" },\n] as const\n\nexport type ElevenLabsPresetVoiceId = (typeof ELEVENLABS_PRESET_VOICES)[number][\"id\"]\n\nexport type ElevenLabsSpeakOptions = {\n voice: ElevenLabsPresetVoiceId | (string & {})\n voice_settings?: ElevenLabsVoiceSettings\n}\n\nfunction newRequestId(): string {\n if (typeof crypto !== \"undefined\" && \"randomUUID\" in crypto) {\n return crypto.randomUUID()\n }\n return `${Date.now()}-${Math.random().toString(16).slice(2)}`\n}\n\nfunction isEmbeddedFrame(): boolean {\n if (typeof window === \"undefined\") return false\n try {\n return window.self !== window.top\n } catch {\n return true\n }\n}\n\nfunction bridgeElevenLabsSpeak(\n userId: string,\n token: string,\n text: string,\n options: ElevenLabsTtsOptions\n): Promise<HTMLAudioElement> {\n if (typeof window === \"undefined\") {\n return Promise.reject(new Error(\"useVoices requires a browser environment\"))\n }\n const target = window.top ?? window.parent\n if (!target || target === window) {\n return Promise.reject(\n new Error(\"useVoices: aucun parent — utilisez la preview éditeur ou une page intégrée.\")\n )\n }\n\n return new Promise((resolve, reject) => {\n const requestId = newRequestId()\n const onMsg = (ev: MessageEvent) => {\n const d = ev.data\n if (!d || d.type !== \"sparkle-elevenlabs-speak-result\" || d.requestId !== requestId) return\n window.removeEventListener(\"message\", onMsg)\n window.clearTimeout(timer)\n if (d.ok) {\n const blob = new Blob([d.buffer], { type: d.contentType })\n const url = URL.createObjectURL(blob)\n const audio = new Audio(url)\n audio.addEventListener(\n \"canplaythrough\",\n () => {\n audio.play().catch(reject)\n resolve(audio)\n },\n { once: true }\n )\n audio.addEventListener(\"error\", () => {\n reject(new Error(\"Failed to load audio\"))\n })\n audio.load()\n } else {\n reject(new Error(typeof d.error === \"string\" ? d.error : \"elevenlabs_speak_failed\"))\n }\n }\n const timer = window.setTimeout(() => {\n window.removeEventListener(\"message\", onMsg)\n reject(new Error(\"useVoices: délai dépassé\"))\n }, 45_000)\n window.addEventListener(\"message\", onMsg)\n target.postMessage({ type: \"sparkle-elevenlabs-speak\", requestId, userId, token, text, options }, \"*\")\n })\n}\n\nasync function fetchElevenLabsSpeak(\n userId: string,\n token: string,\n text: string,\n options: ElevenLabsTtsOptions\n): Promise<HTMLAudioElement> {\n const params = new URLSearchParams()\n if (userId) params.set(\"userId\", userId)\n if (token) params.set(\"k\", token)\n\n const res = await fetch(`/api/obs/alerts/elevenlabs-tts?${params.toString()}`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n text,\n ...options,\n }),\n })\n\n if (!res.ok) {\n throw new Error(`TTS Request failed with status ${res.status}`)\n }\n\n const blob = await res.blob()\n const url = URL.createObjectURL(blob)\n const audio = new Audio(url)\n\n return new Promise((resolve, reject) => {\n audio.addEventListener(\n \"canplaythrough\",\n () => {\n audio.play().catch(reject)\n resolve(audio)\n },\n { once: true }\n )\n\n audio.addEventListener(\"error\", () => {\n reject(new Error(\"Failed to load audio\"))\n })\n\n audio.load()\n })\n}\n\nexport function useVoices() {\n const config = useSparkleConfig()\n const userId = config.userId?.trim() ?? \"\"\n const apiKey = config.apiKey?.trim() ?? \"\"\n const useBridge = isEmbeddedFrame()\n\n const speak = useCallback(\n async (\n text: string,\n options: ElevenLabsSpeakOptions\n ): Promise<HTMLAudioElement> => {\n const preset = ELEVENLABS_PRESET_VOICES.find((v) => v.id === options.voice)\n const voiceId = preset ? preset.voiceId : options.voice\n\n const apiOptions: ElevenLabsTtsOptions = {\n voiceId,\n voice_settings: options.voice_settings,\n }\n\n if (useBridge) {\n return bridgeElevenLabsSpeak(userId, apiKey, text, apiOptions)\n } else {\n return fetchElevenLabsSpeak(userId, apiKey, text, apiOptions)\n }\n },\n [userId, apiKey, useBridge]\n )\n\n return { speak }\n}\n\n","import { useCallback, useState } from \"react\"\nimport { useSparkleConfig } from \"../sparkle-config-context\"\n\nexport type GenerateOptions = {\n model?: \"gemini-3.1-flash-lite\" | \"gemini-3.5-flash\"\n}\n\nfunction newRequestId(): string {\n if (typeof crypto !== \"undefined\" && \"randomUUID\" in crypto) {\n return crypto.randomUUID()\n }\n return `${Date.now()}-${Math.random().toString(16).slice(2)}`\n}\n\nfunction isEmbeddedFrame(): boolean {\n if (typeof window === \"undefined\") return false\n try {\n return window.self !== window.top\n } catch {\n return true\n }\n}\n\nfunction bridgeAiGenerate(\n userId: string,\n token: string,\n prompt: string,\n model?: string\n): Promise<string> {\n if (typeof window === \"undefined\") {\n return Promise.reject(new Error(\"useGenerate requires a browser environment\"))\n }\n const target = window.top ?? window.parent\n if (!target || target === window) {\n return Promise.reject(\n new Error(\n \"useGenerate: aucun parent — utilisez la preview éditeur ou une page intégrée.\"\n )\n )\n }\n\n return new Promise((resolve, reject) => {\n const requestId = newRequestId()\n const onMsg = (ev: MessageEvent) => {\n const d = ev.data\n if (!d || d.type !== \"sparkle-ai-generate-result\" || d.requestId !== requestId) return\n window.removeEventListener(\"message\", onMsg)\n window.clearTimeout(timer)\n if (d.ok) resolve(d.text as string)\n else reject(new Error(typeof d.error === \"string\" ? d.error : \"ai_generate_failed\"))\n }\n const timer = window.setTimeout(() => {\n window.removeEventListener(\"message\", onMsg)\n reject(new Error(\"useGenerate: délai dépassé\"))\n }, 45_000)\n window.addEventListener(\"message\", onMsg)\n target.postMessage({ type: \"sparkle-ai-generate\", requestId, userId, token, prompt, model }, \"*\")\n })\n}\n\nasync function fetchAiGenerate(\n userId: string,\n token: string,\n prompt: string,\n model?: string\n): Promise<string> {\n const params = new URLSearchParams()\n if (userId) params.set(\"userId\", userId)\n if (token) params.set(\"k\", token)\n\n const res = await fetch(`/api/obs/ai-generate?${params.toString()}`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ prompt, model }),\n })\n\n if (!res.ok) {\n const errData = await res.json().catch(() => null)\n throw new Error(errData?.error || `Generation failed with status ${res.status}`)\n }\n\n const data = await res.json()\n return data.text\n}\n\nexport function useGenerate() {\n const config = useSparkleConfig()\n const userId = config.userId?.trim() ?? \"\"\n const apiKey = config.apiKey?.trim() ?? \"\"\n const useBridge = isEmbeddedFrame()\n\n const [isGenerating, setIsGenerating] = useState(false)\n const [error, setError] = useState<Error | null>(null)\n\n const generateText = useCallback(\n async (prompt: string, options?: GenerateOptions): Promise<string> => {\n setIsGenerating(true)\n setError(null)\n\n try {\n const text = useBridge\n ? await bridgeAiGenerate(userId, apiKey, prompt, options?.model)\n : await fetchAiGenerate(userId, apiKey, prompt, options?.model)\n \n return text\n } catch (err) {\n const e = err instanceof Error ? err : new Error(\"Unknown error generating text\")\n setError(e)\n throw e\n } finally {\n setIsGenerating(false)\n }\n },\n [userId, apiKey, useBridge]\n )\n\n return {\n generateText,\n isGenerating,\n error,\n }\n}\n"],"mappings":"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","names":["React","useMemo","useEffect","useState","React","createContext","SparkleAuthContext","AuthProvider","children","config","React","useCallback","useContext","useRef","createContext","useEffect","useState","EventEmitter","z","subscribeSchema","hookSchema","unsubscribeSchema","updateSchema","messageSchema","WebSocketClient","url","data","jwt","authenticatedUrl","event","channels","newChannels","channel","hook","RealtimeContext","createContext","WebSocketProvider","children","config","values","setValues","useState","hooks","setHooks","socket","useRef","token","useContext","SparkleAuthContext","useEffect","WebSocketClient","message","data","type","payload","prev","value","num","subscribe","useCallback","keys","listen","hook","React","React","createContext","useCallback","useEffect","useRef","useState","ObsContext","ObsProvider","url","children","status","setStatus","obsRef","cancelledRef","reconnectTimer","OBSWebSocket","obs","doConnect","call","requestType","requestData","createContext","useContext","SparkleConfigContext","useSparkleConfig","cfg","ClientGate","children","ready","setReady","useState","useEffect","defaultConfig","SparkleProvider","config","mergedConfig","obsUrl","useMemo","inner","React","SparkleConfigContext","AuthProvider","WebSocketProvider","ObsProvider","useContext","useEffect","useMemo","useRealtime","key","defaultValue","context","useContext","RealtimeContext","keys","useMemo","useEffect","k","useEffect","useState","WIDGET_SESSION_REQ","WIDGET_SESSION_RES","newRequestId","isEmbedded","parseSessionMessage","d","o","userId","username","profilePicture","requestSessionViaParent","target","resolve","requestId","onMsg","ev","timer","fetchSessionPayload","res","data","useAuth","setUserId","setUsername","setProfilePicture","cancelled","p","useContext","useEffect","SparkleHookKeys","useHook","hook","context","useContext","RealtimeContext","useEffect","useCallback","useMemo","BRIDGE_TYPE","BRIDGE_RESULT","newRequestId","bridgeInvoke","projectId","body","target","resolve","reject","requestId","onMsg","ev","d","timer","shouldUsePostMessageBridge","fetchInvoke","res","err","useTask","useSparkleConfig","invoke","useCallback","taskId","payload","invokeUntil","delaySeconds","useMemo","useCallback","useMemo","createTwitchGetClient","invoke","call","ns","method","args","BRIDGE_TYPE","BRIDGE_RESULT","newRequestId","shouldUsePostMessageBridge","bridgeTwitchGet","projectId","body","target","resolve","reject","requestId","onMsg","ev","d","timer","fetchTwitchGet","res","err","useTwitch","useSparkleConfig","invoke","useCallback","namespace","method","args","useMemo","createTwitchGetClient","useCallback","useContext","useEffect","useRef","useState","parseCounts","raw","record","read","key","value","parseLatest","cheers","tips","parseRecentList","item","coerceChannelLabelsSnapshot","session","total","latest","recentRaw","emptyChannelLabels","LABELS_SNAPSHOT_KEY","BRIDGE_TYPE","BRIDGE_RESULT","newRequestId","isEmbeddedFrame","bridgeLabelsGet","projectId","target","resolve","reject","requestId","onMsg","ev","d","timer","fetchLabels","res","err","emptyChannelLabels","loadLabelsFallback","useBridge","useLabels","config","useSparkleConfig","userId","realtime","useContext","RealtimeContext","useRealtimeLabels","labels","setLabels","useState","requestIdRef","useRef","realtimeSnapshot","useEffect","parsed","coerceChannelLabelsSnapshot","refresh","useCallback","next","useCallback","useEffect","useRef","useState","newRequestId","isEmbeddedFrame","bridgeSpotifyGet","projectId","target","resolve","reject","requestId","onMsg","ev","d","timer","fetchSpotify","res","err","useSpotify","options","useSparkleConfig","intervalMs","useBridge","track","setTrack","useState","loading","setLoading","error","setError","requestIdRef","useRef","refresh","useCallback","data","useEffect","useCallback","ELEVENLABS_PRESET_VOICES","newRequestId","isEmbeddedFrame","bridgeElevenLabsSpeak","userId","token","text","options","target","resolve","reject","requestId","onMsg","ev","d","timer","blob","url","audio","fetchElevenLabsSpeak","params","res","useVoices","config","useSparkleConfig","apiKey","useBridge","useCallback","preset","v","apiOptions","useCallback","useState","newRequestId","isEmbeddedFrame","bridgeAiGenerate","userId","token","prompt","model","target","resolve","reject","requestId","onMsg","ev","d","timer","fetchAiGenerate","params","res","errData","useGenerate","config","useSparkleConfig","apiKey","useBridge","isGenerating","setIsGenerating","useState","error","setError","useCallback","options","err","e"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sparkle-react",
3
- "version": "0.0.57",
3
+ "version": "0.0.59",
4
4
  "files": [
5
5
  "dist"
6
6
  ],