@fluencypassdevs/cycle 1.31.0 → 1.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/components.generated.mjs +88 -6
- package/dist/{chunk-GDPUWRNH.js → chunk-2MGHBZ7I.js} +9 -6
- package/dist/chunk-2MGHBZ7I.js.map +1 -0
- package/dist/{chunk-KNJTTOAD.js → chunk-2NVKK7UB.js} +3 -3
- package/dist/chunk-2NVKK7UB.js.map +1 -0
- package/dist/{chunk-QXZRH3HU.js → chunk-CDHGWZID.js} +105 -10
- package/dist/chunk-CDHGWZID.js.map +1 -0
- package/dist/{chunk-VZ2GYSNK.js → chunk-OE4CHHIW.js} +46 -11
- package/dist/chunk-OE4CHHIW.js.map +1 -0
- package/dist/{chunk-OFGJDRRR.js → chunk-OZCMOOT7.js} +3 -3
- package/dist/{chunk-OFGJDRRR.js.map → chunk-OZCMOOT7.js.map} +1 -1
- package/dist/{chunk-WVCNTN4F.js → chunk-R7QITHNQ.js} +3 -3
- package/dist/{chunk-WVCNTN4F.js.map → chunk-R7QITHNQ.js.map} +1 -1
- package/dist/composites/chat-thread.js +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +6 -6
- package/dist/lib/utils.js +0 -1
- package/dist/styles/tokens.css +37 -0
- package/dist/ui/chat-message.d.ts +20 -3
- package/dist/ui/chat-message.js +1 -1
- package/dist/ui/circular-progress.d.ts +6 -2
- package/dist/ui/circular-progress.js +1 -1
- package/dist/ui/file-dropzone.js +2 -2
- package/dist/ui/progress.d.ts +3 -1
- package/dist/ui/progress.js +1 -1
- package/dist/ui/video-player.d.ts +23 -1
- package/dist/ui/video-player.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-GDPUWRNH.js.map +0 -1
- package/dist/chunk-KNJTTOAD.js.map +0 -1
- package/dist/chunk-QXZRH3HU.js.map +0 -1
- package/dist/chunk-VZ2GYSNK.js.map +0 -1
|
@@ -110,7 +110,7 @@ export const COMPONENTS = [
|
|
|
110
110
|
{
|
|
111
111
|
"name": "VideoPlayer",
|
|
112
112
|
"import": "import { VideoPlayer } from \"@fluencypassdevs/cycle\"\nimport type { VideoPlayerRef, CaptionTrack } from \"@fluencypassdevs/cycle\"",
|
|
113
|
-
"description": "Player de video (Vidstack). Suporta MP4, WebM, HLS. Src aceita URL string ou { src, type } (VideoPlayerSrc) para blob: de arquivo local e URL sem extensao — para blob, passar type do File.type (normalizado internamente). Chapters, captions/subtitles (multiplos idiomas via CaptionTrack[]), buffering spinner, live indicator, tooltips. Expoe ref imperativo (play/pause), callbacks onTimeUpdate/onEnded e labels para i18n de todos os textos dos controles. Cue de legenda desktop estilizado pelo DS (caixa escura arredondada, 15px). Menus mobile curados para contexto de aula: sem audio gain, sem Google Cast (AirPlay mantido), sem toggles de demo — em Acessibilidade sobra Estilos de legenda (e o menu some inteiro quando o video nao tem legenda), com corpo minimo legivel de 14px.",
|
|
113
|
+
"description": "Player de video (Vidstack). Suporta MP4, WebM, HLS. Src aceita URL string ou { src, type } (VideoPlayerSrc) para blob: de arquivo local e URL sem extensao — para blob, passar type do File.type (normalizado internamente). Chapters, captions/subtitles (multiplos idiomas via CaptionTrack[]), buffering spinner, live indicator, tooltips. Expoe ref imperativo (play/pause), callbacks onTimeUpdate/onEnded e labels para i18n de todos os textos dos controles. Cue de legenda desktop estilizado pelo DS (caixa escura arredondada, 15px). Menus mobile curados para contexto de aula: sem audio gain, sem Google Cast (AirPlay mantido), sem toggles de demo — em Acessibilidade sobra Estilos de legenda (e o menu some inteiro quando o video nao tem legenda), com corpo minimo legivel de 14px. Prop hideControlsWhenPaused (default false): pausa depois de iniciado fica sem overlay de play, escurecimento e barra fixa — para o consumidor colocar conteudo sobre o frame (pop-quiz); controles voltam no hover (desktop) ou toque (mobile) e somem sozinhos; play inicial e replay ao terminar continuam. Prop fullscreenInset ({ top?, right?, bottom?, left? } em valores CSS): quando o proprio player entra em fullscreen, reserva espaco no lado pedido — video, barra, overlay, legenda e gestos recuam juntos — para o consumidor renderizar conteudo ali via portal (ex.: coluna de pergunta a direita em paisagem, abaixo em pe). Fora do fullscreen nao muda nada.",
|
|
114
114
|
"props": [
|
|
115
115
|
{
|
|
116
116
|
"name": "src",
|
|
@@ -137,6 +137,16 @@ export const COMPONENTS = [
|
|
|
137
137
|
"type": "boolean",
|
|
138
138
|
"default": "true"
|
|
139
139
|
},
|
|
140
|
+
{
|
|
141
|
+
"name": "hideControlsWhenPaused",
|
|
142
|
+
"type": "boolean",
|
|
143
|
+
"default": "false"
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
"name": "fullscreenInset",
|
|
147
|
+
"type": "VideoPlayerInset ({ top?, right?, bottom?, left? }: string)",
|
|
148
|
+
"default": "-"
|
|
149
|
+
},
|
|
140
150
|
{
|
|
141
151
|
"name": "startTime",
|
|
142
152
|
"type": "number",
|
|
@@ -168,7 +178,7 @@ export const COMPONENTS = [
|
|
|
168
178
|
"default": "-"
|
|
169
179
|
}
|
|
170
180
|
],
|
|
171
|
-
"example": "<VideoPlayer src=\"/video.mp4\" title=\"Aula 1\" />\n\n{/* Com legendas em multiplos idiomas */}\n<VideoPlayer\n src=\"/video.mp4\"\n title=\"Aula 1\"\n captions={[\n { src: \"/subs/pt.vtt\", language: \"pt-BR\", label: \"Portugues\", default: true },\n { src: \"/subs/en.vtt\", language: \"en\", label: \"English\" },\n ]}\n/>\n\n// Ref imperativo + callbacks:\nconst ref = useRef<VideoPlayerRef>(null)\n<VideoPlayer\n ref={ref}\n src=\"/video.mp4\"\n onTimeUpdate={(currentTime, duration) => reportProgress(currentTime / duration)}\n onEnded={() => markAsCompleted()}\n/>\n// ref.current.play() / ref.current.pause()",
|
|
181
|
+
"example": "<VideoPlayer src=\"/video.mp4\" title=\"Aula 1\" />\n\n{/* Com legendas em multiplos idiomas */}\n<VideoPlayer\n src=\"/video.mp4\"\n title=\"Aula 1\"\n captions={[\n { src: \"/subs/pt.vtt\", language: \"pt-BR\", label: \"Portugues\", default: true },\n { src: \"/subs/en.vtt\", language: \"en\", label: \"English\" },\n ]}\n/>\n\n// Ref imperativo + callbacks:\nconst ref = useRef<VideoPlayerRef>(null)\n<VideoPlayer\n ref={ref}\n src=\"/video.mp4\"\n onTimeUpdate={(currentTime, duration) => reportProgress(currentTime / duration)}\n onEnded={() => markAsCompleted()}\n/>\n// ref.current.play() / ref.current.pause()\n\n// Pop-quiz sobre o frame pausado (sem overlay/controles por cima):\n<VideoPlayer ref={ref} src=\"/aula.mp4\" hideControlsWhenPaused onTimeUpdate={(t) => t >= quizAt && ref.current?.pause()} />\n\n// Fullscreen do player com coluna de pergunta a direita (22rem) — conteudo entra por portal no player:\n<VideoPlayer src=\"/aula.mp4\" hideControlsWhenPaused fullscreenInset={{ right: \"22rem\" }} />",
|
|
172
182
|
"keywords": [
|
|
173
183
|
"video",
|
|
174
184
|
"player",
|
|
@@ -180,7 +190,12 @@ export const COMPONENTS = [
|
|
|
180
190
|
"live",
|
|
181
191
|
"legenda",
|
|
182
192
|
"caption",
|
|
183
|
-
"subtitle"
|
|
193
|
+
"subtitle",
|
|
194
|
+
"quiz",
|
|
195
|
+
"pausa",
|
|
196
|
+
"overlay",
|
|
197
|
+
"fullscreen",
|
|
198
|
+
"inset"
|
|
184
199
|
]
|
|
185
200
|
},
|
|
186
201
|
{
|
|
@@ -212,7 +227,7 @@ export const COMPONENTS = [
|
|
|
212
227
|
{
|
|
213
228
|
"name": "Progress",
|
|
214
229
|
"import": "import { Progress } from \"@fluencypassdevs/cycle\"",
|
|
215
|
-
"description": "Barra de progresso. 4 sizes (xs, sm, default, lg), 4 variants (default, secondary, destructive, muted). Prop theme aplica cor apenas no indicator.",
|
|
230
|
+
"description": "Barra de progresso. 4 sizes (xs, sm, default, lg), 4 variants (default, secondary, destructive, muted). Prop theme aplica cor apenas no indicator. `className` vai para o track; `indicatorClassName` para a barra preenchida. Transicao respeita prefers-reduced-motion. Anuncia aria-valuenow.",
|
|
216
231
|
"props": [
|
|
217
232
|
{
|
|
218
233
|
"name": "value",
|
|
@@ -233,6 +248,11 @@ export const COMPONENTS = [
|
|
|
233
248
|
"name": "theme",
|
|
234
249
|
"type": "string",
|
|
235
250
|
"default": "-"
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
"name": "indicatorClassName",
|
|
254
|
+
"type": "string",
|
|
255
|
+
"default": "-"
|
|
236
256
|
}
|
|
237
257
|
],
|
|
238
258
|
"example": "<Progress value={60} />\n<Progress value={80} size=\"lg\" theme=\"brand\" />",
|
|
@@ -356,7 +376,7 @@ export const COMPONENTS = [
|
|
|
356
376
|
{
|
|
357
377
|
"name": "CircularProgress",
|
|
358
378
|
"import": "import { CircularProgress } from \"@fluencypassdevs/cycle\"",
|
|
359
|
-
"description": "Indicador de progresso circular com porcentagem 0-100 no centro. 4 sizes (xs=48px, sm=64px, default=80px, lg=120px). Prop autoTheme escolhe cor baseada no value (0-33 critical, 34-66 warning, 67-100 positive). Prop theme aceita qualquer dos 8 temas Cycle.",
|
|
379
|
+
"description": "Indicador de progresso circular com porcentagem 0-100 no centro. 4 sizes (xs=48px, sm=64px, default=80px, lg=120px). Prop autoTheme escolhe cor baseada no value (0-33 critical, 34-66 warning, 67-100 positive). Prop theme aceita qualquer dos 8 temas Cycle. `label` aceita ReactNode para tipografar o centro (ex.: numero grande + unidade).",
|
|
360
380
|
"props": [
|
|
361
381
|
{
|
|
362
382
|
"name": "value",
|
|
@@ -385,7 +405,7 @@ export const COMPONENTS = [
|
|
|
385
405
|
},
|
|
386
406
|
{
|
|
387
407
|
"name": "label",
|
|
388
|
-
"type": "
|
|
408
|
+
"type": "React.ReactNode",
|
|
389
409
|
"default": "-"
|
|
390
410
|
}
|
|
391
411
|
],
|
|
@@ -1032,6 +1052,68 @@ export const COMPONENTS = [
|
|
|
1032
1052
|
"bot"
|
|
1033
1053
|
]
|
|
1034
1054
|
},
|
|
1055
|
+
{
|
|
1056
|
+
"name": "ChatAudio",
|
|
1057
|
+
"import": "import { ChatAudio } from \"@fluencypassdevs/cycle\"",
|
|
1058
|
+
"description": "Player de audio compacto do chat (waveform, tempo, velocidade) — o mesmo usado dentro do ChatMessage. Com `src` toca o arquivo e analisa o waveform. Sem `src` vira casca visual controlada pelo consumidor: `playing` + `onPlayingChange`, `currentTime`/`duration` e `onSpeedChange` — para TTS sem arquivo (speechSynthesis). Sem arquivo, passe `peaks` ou usa o waveform padrao.",
|
|
1059
|
+
"props": [
|
|
1060
|
+
{
|
|
1061
|
+
"name": "src",
|
|
1062
|
+
"type": "string",
|
|
1063
|
+
"default": "-"
|
|
1064
|
+
},
|
|
1065
|
+
{
|
|
1066
|
+
"name": "playing",
|
|
1067
|
+
"type": "boolean",
|
|
1068
|
+
"default": "- (nao controlado)"
|
|
1069
|
+
},
|
|
1070
|
+
{
|
|
1071
|
+
"name": "onPlayingChange",
|
|
1072
|
+
"type": "(playing: boolean) => void",
|
|
1073
|
+
"default": "-"
|
|
1074
|
+
},
|
|
1075
|
+
{
|
|
1076
|
+
"name": "currentTime",
|
|
1077
|
+
"type": "number",
|
|
1078
|
+
"default": "-"
|
|
1079
|
+
},
|
|
1080
|
+
{
|
|
1081
|
+
"name": "duration",
|
|
1082
|
+
"type": "number",
|
|
1083
|
+
"default": "-"
|
|
1084
|
+
},
|
|
1085
|
+
{
|
|
1086
|
+
"name": "onSpeedChange",
|
|
1087
|
+
"type": "(speed: number) => void",
|
|
1088
|
+
"default": "-"
|
|
1089
|
+
},
|
|
1090
|
+
{
|
|
1091
|
+
"name": "peaks",
|
|
1092
|
+
"type": "number[] (0-1)",
|
|
1093
|
+
"default": "-"
|
|
1094
|
+
},
|
|
1095
|
+
{
|
|
1096
|
+
"name": "barCount",
|
|
1097
|
+
"type": "number",
|
|
1098
|
+
"default": "60"
|
|
1099
|
+
},
|
|
1100
|
+
{
|
|
1101
|
+
"name": "variant",
|
|
1102
|
+
"type": "\"neutral\" | \"brand\"",
|
|
1103
|
+
"default": "\"neutral\""
|
|
1104
|
+
}
|
|
1105
|
+
],
|
|
1106
|
+
"example": "<ChatAudio src=\"/resposta.mp3\" />\n\n// TTS sem arquivo (speechSynthesis):\n<ChatAudio playing={playing} onPlayingChange={handlePlaying} onSpeedChange={setRate} />",
|
|
1107
|
+
"keywords": [
|
|
1108
|
+
"audio",
|
|
1109
|
+
"tts",
|
|
1110
|
+
"voz",
|
|
1111
|
+
"chat",
|
|
1112
|
+
"waveform",
|
|
1113
|
+
"speechSynthesis",
|
|
1114
|
+
"player"
|
|
1115
|
+
]
|
|
1116
|
+
},
|
|
1035
1117
|
{
|
|
1036
1118
|
"name": "MessageRating",
|
|
1037
1119
|
"import": "import { MessageRating } from \"@fluencypassdevs/cycle\"",
|
|
@@ -21,7 +21,7 @@ var progressVariants = cva(
|
|
|
21
21
|
}
|
|
22
22
|
);
|
|
23
23
|
var indicatorVariants = cva(
|
|
24
|
-
"h-full w-full flex-1 rounded-full transition-all",
|
|
24
|
+
"h-full w-full flex-1 rounded-full motion-safe:transition-all",
|
|
25
25
|
{
|
|
26
26
|
variants: {
|
|
27
27
|
variant: {
|
|
@@ -42,25 +42,28 @@ function Progress(_a) {
|
|
|
42
42
|
value,
|
|
43
43
|
size,
|
|
44
44
|
variant,
|
|
45
|
-
theme
|
|
45
|
+
theme,
|
|
46
|
+
indicatorClassName
|
|
46
47
|
} = _b, props = __objRest(_b, [
|
|
47
48
|
"className",
|
|
48
49
|
"value",
|
|
49
50
|
"size",
|
|
50
51
|
"variant",
|
|
51
|
-
"theme"
|
|
52
|
+
"theme",
|
|
53
|
+
"indicatorClassName"
|
|
52
54
|
]);
|
|
53
55
|
return /* @__PURE__ */ jsx(
|
|
54
56
|
Progress$1.Root,
|
|
55
57
|
__spreadProps(__spreadValues({
|
|
56
58
|
"data-slot": "progress",
|
|
59
|
+
value,
|
|
57
60
|
className: cn(progressVariants({ size }), className)
|
|
58
61
|
}, props), {
|
|
59
62
|
children: /* @__PURE__ */ jsx(
|
|
60
63
|
Progress$1.Indicator,
|
|
61
64
|
{
|
|
62
65
|
"data-slot": "progress-indicator",
|
|
63
|
-
className: cn(theme, indicatorVariants({ variant })),
|
|
66
|
+
className: cn(theme, indicatorVariants({ variant }), indicatorClassName),
|
|
64
67
|
style: { transform: `translateX(-${100 - (value || 0)}%)` }
|
|
65
68
|
}
|
|
66
69
|
)
|
|
@@ -69,5 +72,5 @@ function Progress(_a) {
|
|
|
69
72
|
}
|
|
70
73
|
|
|
71
74
|
export { Progress, indicatorVariants, progressVariants };
|
|
72
|
-
//# sourceMappingURL=chunk-
|
|
73
|
-
//# sourceMappingURL=chunk-
|
|
75
|
+
//# sourceMappingURL=chunk-2MGHBZ7I.js.map
|
|
76
|
+
//# sourceMappingURL=chunk-2MGHBZ7I.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/ui/progress.tsx"],"names":["ProgressPrimitive"],"mappings":";;;;;;AAUA,IAAM,gBAAA,GAAmB,GAAA;AAAA,EACvB,+DAAA;AAAA,EACA;AAAA,IACE,QAAA,EAAU;AAAA,MACR,IAAA,EAAM;AAAA,QACJ,EAAA,EAAI,KAAA;AAAA,QACJ,EAAA,EAAI,OAAA;AAAA,QACJ,OAAA,EAAS,KAAA;AAAA,QACT,EAAA,EAAI;AAAA;AACN,KACF;AAAA,IACA,eAAA,EAAiB;AAAA,MACf,IAAA,EAAM;AAAA;AACR;AAEJ;AAIA,IAAM,iBAAA,GAAoB,GAAA;AAAA,EACxB,8DAAA;AAAA,EACA;AAAA,IACE,QAAA,EAAU;AAAA,MACR,OAAA,EAAS;AAAA,QACP,OAAA,EAAS,YAAA;AAAA,QACT,SAAA,EAAW,cAAA;AAAA,QACX,WAAA,EAAa,gBAAA;AAAA,QACb,KAAA,EAAO;AAAA;AACT,KACF;AAAA,IACA,eAAA,EAAiB;AAAA,MACf,OAAA,EAAS;AAAA;AACX;AAEJ;AAYA,SAAS,SAAS,EAAA,EAQA;AARA,EAAA,IAAA,EAAA,GAAA,EAAA,EAChB;AAAA,IAAA,SAAA;AAAA,IACA,KAAA;AAAA,IACA,IAAA;AAAA,IACA,OAAA;AAAA,IACA,KAAA;AAAA,IACA;AAAA,GA9DF,GAwDkB,EAAA,EAOb,KAAA,GAAA,SAAA,CAPa,EAAA,EAOb;AAAA,IANH,WAAA;AAAA,IACA,OAAA;AAAA,IACA,MAAA;AAAA,IACA,SAAA;AAAA,IACA,OAAA;AAAA,IACA;AAAA,GAAA,CAAA;AAGA,EAAA,uBACE,GAAA;AAAA,IAACA,UAAA,CAAkB,IAAA;AAAA,IAAlB,aAAA,CAAA,cAAA,CAAA;AAAA,MACC,WAAA,EAAU,UAAA;AAAA,MACV,KAAA;AAAA,MACA,WAAW,EAAA,CAAG,gBAAA,CAAiB,EAAE,IAAA,EAAM,GAAG,SAAS;AAAA,KAAA,EAC/C,KAAA,CAAA,EAJL;AAAA,MAMC,QAAA,kBAAA,GAAA;AAAA,QAACA,UAAA,CAAkB,SAAA;AAAA,QAAlB;AAAA,UACC,WAAA,EAAU,oBAAA;AAAA,UACV,SAAA,EAAW,GAAG,KAAA,EAAO,iBAAA,CAAkB,EAAE,OAAA,EAAS,GAAG,kBAAkB,CAAA;AAAA,UACvE,OAAO,EAAE,SAAA,EAAW,eAAe,GAAA,IAAO,KAAA,IAAS,EAAE,CAAA,EAAA,CAAA;AAAK;AAAA;AAC5D,KAAA;AAAA,GACF;AAEJ","file":"chunk-2MGHBZ7I.js","sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { Progress as ProgressPrimitive } from \"radix-ui\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\n/* ─── Track variants ─── */\n\nconst progressVariants = cva(\n \"relative w-full overflow-hidden rounded-full bg-neutral-input\",\n {\n variants: {\n size: {\n xs: \"h-1\",\n sm: \"h-1.5\",\n default: \"h-2\",\n lg: \"h-3\",\n },\n },\n defaultVariants: {\n size: \"default\",\n },\n }\n)\n\n/* ─── Indicator variants ─── */\n\nconst indicatorVariants = cva(\n \"h-full w-full flex-1 rounded-full motion-safe:transition-all\",\n {\n variants: {\n variant: {\n default: \"bg-primary\",\n secondary: \"bg-secondary\",\n destructive: \"bg-destructive\",\n muted: \"bg-muted-foreground\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n },\n }\n)\n\nexport interface ProgressProps\n extends Omit<React.ComponentProps<typeof ProgressPrimitive.Root>, \"children\">,\n VariantProps<typeof progressVariants>,\n VariantProps<typeof indicatorVariants> {\n /** Classe de tema aplicada apenas no indicator (ex: \"theme-class\") */\n theme?: string\n /** Classes extras no indicator (a barra preenchida). `className` vai para o track. */\n indicatorClassName?: string\n}\n\nfunction Progress({\n className,\n value,\n size,\n variant,\n theme,\n indicatorClassName,\n ...props\n}: ProgressProps) {\n return (\n <ProgressPrimitive.Root\n data-slot=\"progress\"\n value={value}\n className={cn(progressVariants({ size }), className)}\n {...props}\n >\n <ProgressPrimitive.Indicator\n data-slot=\"progress-indicator\"\n className={cn(theme, indicatorVariants({ variant }), indicatorClassName)}\n style={{ transform: `translateX(-${100 - (value || 0)}%)` }}\n />\n </ProgressPrimitive.Root>\n )\n}\n\nexport { Progress, progressVariants, indicatorVariants }\n"]}
|
|
@@ -61,7 +61,7 @@ function CircularProgress(_a) {
|
|
|
61
61
|
"aria-valuenow": clamped,
|
|
62
62
|
"aria-valuemin": 0,
|
|
63
63
|
"aria-valuemax": 100,
|
|
64
|
-
"aria-label": !showLabel &&
|
|
64
|
+
"aria-label": !showLabel && label == null ? `Progress: ${Math.round(clamped)}%` : void 0,
|
|
65
65
|
className: cn(circularProgressVariants({ size }), resolvedTheme, className),
|
|
66
66
|
style: { width: px, height: px }
|
|
67
67
|
}, props), {
|
|
@@ -126,5 +126,5 @@ function CircularProgress(_a) {
|
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
export { CircularProgress, circularProgressVariants };
|
|
129
|
-
//# sourceMappingURL=chunk-
|
|
130
|
-
//# sourceMappingURL=chunk-
|
|
129
|
+
//# sourceMappingURL=chunk-2NVKK7UB.js.map
|
|
130
|
+
//# sourceMappingURL=chunk-2NVKK7UB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/ui/circular-progress.tsx"],"names":[],"mappings":";;;;;AAOA,IAAM,WAAA,GAAc;AAAA,EAClB,IAAI,EAAE,EAAA,EAAI,IAAI,MAAA,EAAQ,CAAA,EAAG,WAAW,uBAAA,EAAwB;AAAA,EAC5D,IAAI,EAAE,EAAA,EAAI,IAAI,MAAA,EAAQ,CAAA,EAAG,WAAW,yBAAA,EAA0B;AAAA,EAC9D,SAAS,EAAE,EAAA,EAAI,IAAI,MAAA,EAAQ,CAAA,EAAG,WAAW,wBAAA,EAAyB;AAAA,EAClE,IAAI,EAAE,EAAA,EAAI,KAAK,MAAA,EAAQ,CAAA,EAAG,WAAW,wBAAA;AACvC,CAAA;AAEA,IAAM,wBAAA,GAA2B,IAAI,+BAAA,EAAiC;AAAA,EACpE,QAAA,EAAU;AAAA,IACR,IAAA,EAAM;AAAA,MACJ,EAAA,EAAI,SAAA;AAAA,MACJ,EAAA,EAAI,SAAA;AAAA,MACJ,OAAA,EAAS,SAAA;AAAA,MACT,EAAA,EAAI;AAAA;AACN,GACF;AAAA,EACA,eAAA,EAAiB;AAAA,IACf,IAAA,EAAM;AAAA;AAEV,CAAC;AAID,SAAS,aAAa,KAAA,EAAuB;AAC3C,EAAA,IAAI,KAAA,GAAQ,IAAI,OAAO,gBAAA;AACvB,EAAA,IAAI,KAAA,GAAQ,IAAI,OAAO,eAAA;AACvB,EAAA,OAAO,gBAAA;AACT;AAqBA,SAAS,iBAAiB,EAAA,EASA;AATA,EAAA,IAAA,EAAA,GAAA,EAAA,EACxB;AAAA,IAAA,KAAA;AAAA,IACA,IAAA,GAAO,SAAA;AAAA,IACP,KAAA;AAAA,IACA,SAAA,GAAY,KAAA;AAAA,IACZ,SAAA,GAAY,IAAA;AAAA,IACZ,KAAA;AAAA,IACA;AAAA,GA9DF,GAuD0B,EAAA,EAQrB,KAAA,GAAA,SAAA,CARqB,EAAA,EAQrB;AAAA,IAPH,OAAA;AAAA,IACA,MAAA;AAAA,IACA,OAAA;AAAA,IACA,WAAA;AAAA,IACA,WAAA;AAAA,IACA,OAAA;AAAA,IACA;AAAA,GAAA,CAAA;AAGA,EAAA,MAAM,OAAA,GAAU,KAAK,GAAA,CAAI,GAAA,EAAK,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,CAAC,CAAA;AAChD,EAAA,MAAM,MAAA,GAAS,WAAA,CAAY,IAAA,IAAA,IAAA,GAAA,IAAA,GAAQ,SAAS,CAAA;AAC5C,EAAA,MAAM,EAAE,EAAA,EAAI,MAAA,EAAQ,SAAA,EAAU,GAAI,MAAA;AAElC,EAAA,MAAM,MAAA,GAAA,CAAU,KAAK,MAAA,IAAU,CAAA;AAC/B,EAAA,MAAM,aAAA,GAAgB,CAAA,GAAI,IAAA,CAAK,EAAA,GAAK,MAAA;AACpC,EAAA,MAAM,IAAA,GAAQ,UAAU,GAAA,GAAO,aAAA;AAE/B,EAAA,MAAM,aAAA,GAAgB,KAAA,IAAA,IAAA,GAAA,KAAA,GAAU,SAAA,GAAY,YAAA,CAAa,OAAO,CAAA,GAAI,MAAA;AACpE,EAAA,MAAM,eAAgC,KAAA,IAAA,IAAA,GAAA,KAAA,GAAS,CAAA,EAAG,IAAA,CAAK,KAAA,CAAM,OAAO,CAAC,CAAA,CAAA,CAAA;AAErE,EAAA,uBACE,IAAA;AAAA,IAAC,KAAA;AAAA,IAAA,aAAA,CAAA,cAAA,CAAA;AAAA,MACC,WAAA,EAAU,mBAAA;AAAA,MACV,IAAA,EAAK,aAAA;AAAA,MACL,eAAA,EAAe,OAAA;AAAA,MACf,eAAA,EAAe,CAAA;AAAA,MACf,eAAA,EAAe,GAAA;AAAA,MACf,YAAA,EAAY,CAAC,SAAA,IAAa,KAAA,IAAS,IAAA,GAAO,aAAa,IAAA,CAAK,KAAA,CAAM,OAAO,CAAC,CAAA,CAAA,CAAA,GAAM,MAAA;AAAA,MAChF,SAAA,EAAW,GAAG,wBAAA,CAAyB,EAAE,MAAM,CAAA,EAAG,eAAe,SAAS,CAAA;AAAA,MAC1E,KAAA,EAAO,EAAE,KAAA,EAAO,EAAA,EAAI,QAAQ,EAAA;AAAG,KAAA,EAC3B,KAAA,CAAA,EATL;AAAA,MAYC,QAAA,EAAA;AAAA,wBAAA,GAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACC,SAAA,EAAW,EAAA;AAAA,cACT,uBAAA;AAAA,cACA,gBAAgB,WAAA,GAAc;AAAA,aAChC;AAAA,YACA,KAAA,EAAO,EAAE,KAAA,EAAO,MAAA;AAAO;AAAA,SACzB;AAAA,wBAGA,IAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACC,SAAA,EAAU,uCAAA;AAAA,YACV,OAAA,EAAS,CAAA,IAAA,EAAO,EAAE,CAAA,CAAA,EAAI,EAAE,CAAA,CAAA;AAAA,YACxB,aAAA,EAAY,MAAA;AAAA,YAEZ,QAAA,EAAA;AAAA,8BAAA,GAAA;AAAA,gBAAC,QAAA;AAAA,gBAAA;AAAA,kBACC,IAAI,EAAA,GAAK,CAAA;AAAA,kBACT,IAAI,EAAA,GAAK,CAAA;AAAA,kBACT,CAAA,EAAG,MAAA;AAAA,kBACH,IAAA,EAAK,MAAA;AAAA,kBACL,WAAA,EAAa,MAAA;AAAA,kBACb,SAAA,EAAW,EAAA,CAAG,aAAA,GAAgB,cAAA,GAAiB,sBAAsB;AAAA;AAAA,eACvE;AAAA,8BACA,GAAA;AAAA,gBAAC,QAAA;AAAA,gBAAA;AAAA,kBACC,IAAI,EAAA,GAAK,CAAA;AAAA,kBACT,IAAI,EAAA,GAAK,CAAA;AAAA,kBACT,CAAA,EAAG,MAAA;AAAA,kBACH,IAAA,EAAK,MAAA;AAAA,kBACL,WAAA,EAAa,MAAA;AAAA,kBACb,aAAA,EAAc,OAAA;AAAA,kBACd,eAAA,EAAiB,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,aAAa,CAAA,CAAA;AAAA,kBACzC,SAAA,EAAW,EAAA,CAAG,4CAAA,EAA8C,aAAA,GAAgB,mBAAmB,2BAA2B;AAAA;AAAA;AAC5H;AAAA;AAAA,SACF;AAAA,QAGC,SAAA,oBACC,GAAA;AAAA,UAAC,MAAA;AAAA,UAAA;AAAA,YACC,SAAA,EAAW,EAAA;AAAA,cACT,kFAAA;AAAA,cACA;AAAA,aACF;AAAA,YAEC,QAAA,EAAA;AAAA;AAAA;AACH;AAAA,KAAA;AAAA,GAEJ;AAEJ","file":"chunk-2NVKK7UB.js","sourcesContent":["import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\n/* ─── Size config ─── */\n\nconst SIZE_CONFIG = {\n xs: { px: 48, stroke: 4, fontClass: \"text-sm font-semibold\" },\n sm: { px: 64, stroke: 5, fontClass: \"text-base font-semibold\" },\n default: { px: 80, stroke: 6, fontClass: \"text-2xl font-semibold\" },\n lg: { px: 120, stroke: 8, fontClass: \"text-3xl font-semibold\" },\n} as const\n\nconst circularProgressVariants = cva(\"relative inline-flex shrink-0\", {\n variants: {\n size: {\n xs: \"size-12\",\n sm: \"size-16\",\n default: \"size-20\",\n lg: \"size-30\",\n },\n },\n defaultVariants: {\n size: \"default\",\n },\n})\n\n/* ─── Auto-theme thresholds ─── */\n\nfunction getAutoTheme(value: number): string {\n if (value < 34) return \"theme-critical\"\n if (value < 67) return \"theme-warning\"\n return \"theme-positive\"\n}\n\nexport interface CircularProgressProps\n extends Omit<React.ComponentProps<\"div\">, \"children\">,\n VariantProps<typeof circularProgressVariants> {\n /** Valor de 0 a 100 (clamped) */\n value: number\n /** Classe de tema fixa (ex: \"theme-brand\", \"theme-class\"). Tem precedencia sobre autoTheme. */\n theme?: string\n /** Se true, escolhe o tema automaticamente: 0–33 critical, 34–66 warning, 67–100 positive. */\n autoTheme?: boolean\n /** Mostrar label central com a porcentagem (default true) */\n showLabel?: boolean\n /**\n * Label customizado — sobrescreve `${value}%`. Aceita ReactNode para tipografar o\n * centro (ex.: numero grande + unidade menor). A fonte padrao do size continua\n * aplicada no wrapper; elementos internos podem sobrescrever.\n */\n label?: React.ReactNode\n}\n\nfunction CircularProgress({\n value,\n size = \"default\",\n theme,\n autoTheme = false,\n showLabel = true,\n label,\n className,\n ...props\n}: CircularProgressProps) {\n const clamped = Math.min(100, Math.max(0, value))\n const config = SIZE_CONFIG[size ?? \"default\"]\n const { px, stroke, fontClass } = config\n\n const radius = (px - stroke) / 2\n const circumference = 2 * Math.PI * radius\n const dash = (clamped / 100) * circumference\n\n const resolvedTheme = theme ?? (autoTheme ? getAutoTheme(clamped) : undefined)\n const displayLabel: React.ReactNode = label ?? `${Math.round(clamped)}%`\n\n return (\n <div\n data-slot=\"circular-progress\"\n role=\"progressbar\"\n aria-valuenow={clamped}\n aria-valuemin={0}\n aria-valuemax={100}\n aria-label={!showLabel && label == null ? `Progress: ${Math.round(clamped)}%` : undefined}\n className={cn(circularProgressVariants({ size }), resolvedTheme, className)}\n style={{ width: px, height: px }}\n {...props}\n >\n {/* Inner pastel fill */}\n <div\n className={cn(\n \"absolute rounded-full\",\n resolvedTheme ? \"bg-accent\" : \"bg-neutral-input/40\"\n )}\n style={{ inset: stroke }}\n />\n\n {/* Progress arcs */}\n <svg\n className=\"absolute inset-0 size-full -rotate-90\"\n viewBox={`0 0 ${px} ${px}`}\n aria-hidden=\"true\"\n >\n <circle\n cx={px / 2}\n cy={px / 2}\n r={radius}\n fill=\"none\"\n strokeWidth={stroke}\n className={cn(resolvedTheme ? \"stroke-muted\" : \"stroke-neutral-input\")}\n />\n <circle\n cx={px / 2}\n cy={px / 2}\n r={radius}\n fill=\"none\"\n strokeWidth={stroke}\n strokeLinecap=\"round\"\n strokeDasharray={`${dash} ${circumference}`}\n className={cn(\"transition-[stroke-dasharray] duration-500\", resolvedTheme ? \"stroke-primary\" : \"stroke-neutral-foreground\")}\n />\n </svg>\n\n {/* Center label */}\n {showLabel && (\n <span\n className={cn(\n \"relative z-10 flex size-full items-center justify-center text-neutral-foreground\",\n fontClass\n )}\n >\n {displayLabel}\n </span>\n )}\n </div>\n )\n}\n\nexport { CircularProgress, circularProgressVariants }\n"]}
|
|
@@ -3,7 +3,7 @@ import { CycleIcon } from './chunk-US5LXJTO.js';
|
|
|
3
3
|
import { cn } from './chunk-W4KJCBMW.js';
|
|
4
4
|
import { __spreadValues, __spreadProps } from './chunk-2NMEKWO5.js';
|
|
5
5
|
import * as React from 'react';
|
|
6
|
-
import { MediaPlayer, MediaProvider, Poster, Track, MediaAnnouncer,
|
|
6
|
+
import { MediaPlayer, MediaProvider, Poster, Track, MediaAnnouncer, Gesture, Controls, Captions, Spinner, PlayButton, useMediaRemote, useMediaState, TimeSlider, SeekButton, MuteButton, VolumeSlider, Time, LiveButton, useCaptionOptions, CaptionButton, Menu, useVideoQualityOptions, PIPButton, FullscreenButton, Tooltip } from '@vidstack/react';
|
|
7
7
|
import { DefaultVideoLayout, defaultLayoutIcons } from '@vidstack/react/player/layouts/default';
|
|
8
8
|
import '@vidstack/react/player/styles/base.css';
|
|
9
9
|
import '@vidstack/react/player/styles/default/theme.css';
|
|
@@ -16,6 +16,17 @@ function resolveVideoSrc(src) {
|
|
|
16
16
|
if (src.src.startsWith("blob:")) return { src: src.src, type: "video/object" };
|
|
17
17
|
return src;
|
|
18
18
|
}
|
|
19
|
+
var INSET_SIDES = ["top", "right", "bottom", "left"];
|
|
20
|
+
function insetVars(inset) {
|
|
21
|
+
if (!inset) return void 0;
|
|
22
|
+
const vars = {};
|
|
23
|
+
for (const side of INSET_SIDES) {
|
|
24
|
+
if (inset[side]) vars[`--cycle-video-fs-${side}`] = inset[side];
|
|
25
|
+
}
|
|
26
|
+
return vars;
|
|
27
|
+
}
|
|
28
|
+
var FS_INSET_PADDING = "data-[fullscreen]:pt-[var(--cycle-video-fs-top,0px)] data-[fullscreen]:pr-[var(--cycle-video-fs-right,0px)] data-[fullscreen]:pb-[var(--cycle-video-fs-bottom,0px)] data-[fullscreen]:pl-[var(--cycle-video-fs-left,0px)]";
|
|
29
|
+
var FS_INSET_CHROME = "absolute inset-0 group-data-[fullscreen]:top-[var(--cycle-video-fs-top,0px)] group-data-[fullscreen]:right-[var(--cycle-video-fs-right,0px)] group-data-[fullscreen]:bottom-[var(--cycle-video-fs-bottom,0px)] group-data-[fullscreen]:left-[var(--cycle-video-fs-left,0px)]";
|
|
19
30
|
var VideoLabelsContext = React.createContext(
|
|
20
31
|
resolveVideoLabels("pt-BR")
|
|
21
32
|
);
|
|
@@ -31,11 +42,23 @@ function ControlTooltip({ label, children }) {
|
|
|
31
42
|
] });
|
|
32
43
|
}
|
|
33
44
|
var filledIconClass = "text-white fill-current !stroke-transparent";
|
|
34
|
-
function
|
|
45
|
+
function usePausedChromeVisible(hideWhenPaused) {
|
|
35
46
|
const isPaused = useMediaState("paused");
|
|
36
47
|
const hasStarted = useMediaState("started");
|
|
37
|
-
|
|
38
|
-
return
|
|
48
|
+
const hasEnded = useMediaState("ended");
|
|
49
|
+
return isPaused && (!hideWhenPaused || !hasStarted || hasEnded);
|
|
50
|
+
}
|
|
51
|
+
function PlayOverlay({ hideWhenPaused }) {
|
|
52
|
+
const visible = usePausedChromeVisible(hideWhenPaused);
|
|
53
|
+
if (!visible) return null;
|
|
54
|
+
return /* @__PURE__ */ jsx(
|
|
55
|
+
PlayButton,
|
|
56
|
+
{
|
|
57
|
+
"data-slot": "video-play-overlay",
|
|
58
|
+
className: "absolute inset-0 z-10 flex cursor-pointer items-center justify-center bg-black/30 transition-opacity",
|
|
59
|
+
children: /* @__PURE__ */ jsx("div", { className: "flex size-16 items-center justify-center rounded-full bg-white/20 backdrop-blur-sm transition-transform hover:scale-110", children: /* @__PURE__ */ jsx(CycleIcon, { icon: Play, size: "lg", decorative: true, className: cn(filledIconClass, "ml-1") }) })
|
|
60
|
+
}
|
|
61
|
+
);
|
|
39
62
|
}
|
|
40
63
|
function PlayControl() {
|
|
41
64
|
const isPaused = useMediaState("paused");
|
|
@@ -375,6 +398,8 @@ var VideoPlayer = React.forwardRef(
|
|
|
375
398
|
startTime,
|
|
376
399
|
locale = "pt-BR",
|
|
377
400
|
labels,
|
|
401
|
+
hideControlsWhenPaused = false,
|
|
402
|
+
fullscreenInset,
|
|
378
403
|
onTimeUpdate,
|
|
379
404
|
onEnded,
|
|
380
405
|
className
|
|
@@ -429,6 +454,7 @@ var VideoPlayer = React.forwardRef(
|
|
|
429
454
|
onCanPlay: handleCanPlay
|
|
430
455
|
}, onTimeUpdate ? { onTimeUpdate: handleTimeUpdate } : {}), onEnded ? { onEnded: handleEnded } : {});
|
|
431
456
|
const isDesktop = useIsDesktop();
|
|
457
|
+
const fsInsetStyle = React.useMemo(() => insetVars(fullscreenInset), [fullscreenInset]);
|
|
432
458
|
const [seekFeedback, setSeekFeedback] = React.useState(null);
|
|
433
459
|
const [seekAccumulated, setSeekAccumulated] = React.useState(0);
|
|
434
460
|
const feedbackTimeout = React.useRef(void 0);
|
|
@@ -466,6 +492,42 @@ var VideoPlayer = React.forwardRef(
|
|
|
466
492
|
React.useEffect(() => {
|
|
467
493
|
return () => clearTimeout(feedbackTimeout.current);
|
|
468
494
|
}, []);
|
|
495
|
+
React.useEffect(() => {
|
|
496
|
+
const p = player.current;
|
|
497
|
+
const el = p == null ? void 0 : p.el;
|
|
498
|
+
if (!p || !el || isDesktop || !hideControlsWhenPaused) return;
|
|
499
|
+
let timer;
|
|
500
|
+
const clear = () => clearTimeout(timer);
|
|
501
|
+
const quietPause = () => p.state.started && p.state.paused && !p.state.ended;
|
|
502
|
+
const hide = () => {
|
|
503
|
+
clear();
|
|
504
|
+
if (quietPause()) p.controls.hide(0);
|
|
505
|
+
};
|
|
506
|
+
const arm = () => {
|
|
507
|
+
clear();
|
|
508
|
+
if (quietPause()) timer = setTimeout(hide, p.controls.defaultDelay);
|
|
509
|
+
};
|
|
510
|
+
const onPause = () => queueMicrotask(hide);
|
|
511
|
+
const onControlsChange = (e) => {
|
|
512
|
+
if (e.detail) arm();
|
|
513
|
+
else clear();
|
|
514
|
+
};
|
|
515
|
+
const onEnded2 = () => {
|
|
516
|
+
clear();
|
|
517
|
+
p.controls.show(0);
|
|
518
|
+
};
|
|
519
|
+
el.addEventListener("pause", onPause);
|
|
520
|
+
el.addEventListener("ended", onEnded2);
|
|
521
|
+
el.addEventListener("controls-change", onControlsChange);
|
|
522
|
+
el.addEventListener("media-resume-controls-request", arm);
|
|
523
|
+
return () => {
|
|
524
|
+
clear();
|
|
525
|
+
el.removeEventListener("pause", onPause);
|
|
526
|
+
el.removeEventListener("ended", onEnded2);
|
|
527
|
+
el.removeEventListener("controls-change", onControlsChange);
|
|
528
|
+
el.removeEventListener("media-resume-controls-request", arm);
|
|
529
|
+
};
|
|
530
|
+
}, [isDesktop, hideControlsWhenPaused]);
|
|
469
531
|
return /* @__PURE__ */ jsx(VideoLabelsContext.Provider, { value: resolvedLabels, children: /* @__PURE__ */ jsxs(
|
|
470
532
|
MediaPlayer,
|
|
471
533
|
__spreadProps(__spreadValues({
|
|
@@ -476,9 +538,14 @@ var VideoPlayer = React.forwardRef(
|
|
|
476
538
|
loop,
|
|
477
539
|
playsInline: true,
|
|
478
540
|
crossOrigin: "",
|
|
541
|
+
style: fsInsetStyle,
|
|
479
542
|
className: cn(
|
|
480
543
|
"group relative aspect-video w-full overflow-hidden bg-black text-white",
|
|
481
544
|
isDesktop ? "rounded-[16px]" : "rounded-none",
|
|
545
|
+
// fullscreenInset: o proprio player reserva o espaco (padding) — o provider de
|
|
546
|
+
// midia e relative/100%, entao o video encolhe junto. O chrome recua via wrapper
|
|
547
|
+
// (desktop) ou CSS em tokens.css (mobile), ambos lendo as mesmas variaveis.
|
|
548
|
+
FS_INSET_PADDING,
|
|
482
549
|
className
|
|
483
550
|
)
|
|
484
551
|
}, playerEvents), {
|
|
@@ -529,14 +596,14 @@ var VideoPlayer = React.forwardRef(
|
|
|
529
596
|
slots: { googleCastButton: null }
|
|
530
597
|
}
|
|
531
598
|
),
|
|
532
|
-
isDesktop && /* @__PURE__ */ jsxs(
|
|
533
|
-
/* @__PURE__ */ jsx(
|
|
534
|
-
/* @__PURE__ */ jsx(PlayOverlay, {}),
|
|
599
|
+
isDesktop && /* @__PURE__ */ jsxs("div", { "data-slot": "video-chrome", className: FS_INSET_CHROME, children: [
|
|
600
|
+
/* @__PURE__ */ jsx(DesktopCaptions, { hideWhenPaused: hideControlsWhenPaused }),
|
|
601
|
+
/* @__PURE__ */ jsx(PlayOverlay, { hideWhenPaused: hideControlsWhenPaused }),
|
|
535
602
|
/* @__PURE__ */ jsx(Gesture, { className: "absolute inset-0 z-0 block h-full w-full", event: "pointerup", action: "toggle:paused" }),
|
|
536
603
|
/* @__PURE__ */ jsx(Gesture, { className: "absolute inset-0 z-0 block h-full w-full", event: "dblpointerup", action: "toggle:fullscreen" }),
|
|
537
604
|
/* @__PURE__ */ jsx(SeekGestureZone, { side: "left", onSeekFeedback: handleSeekFeedback }),
|
|
538
605
|
/* @__PURE__ */ jsx(SeekGestureZone, { side: "right", onSeekFeedback: handleSeekFeedback }),
|
|
539
|
-
/* @__PURE__ */ jsxs(
|
|
606
|
+
/* @__PURE__ */ jsxs(DesktopControls, { hideWhenPaused: hideControlsWhenPaused, children: [
|
|
540
607
|
/* @__PURE__ */ jsx("div", { className: "flex-1" }),
|
|
541
608
|
/* @__PURE__ */ jsx(Controls.Group, { className: "flex w-full items-center px-3", children: /* @__PURE__ */ jsx(SeekBar, { thumbnails, chapters: !!chapters }) }),
|
|
542
609
|
/* @__PURE__ */ jsxs(Controls.Group, { className: "@container flex w-full items-center gap-1 px-2 pb-2", children: [
|
|
@@ -561,7 +628,35 @@ var VideoPlayer = React.forwardRef(
|
|
|
561
628
|
) });
|
|
562
629
|
}
|
|
563
630
|
);
|
|
631
|
+
function DesktopCaptions({ hideWhenPaused }) {
|
|
632
|
+
const chromeVisible = usePausedChromeVisible(hideWhenPaused);
|
|
633
|
+
return /* @__PURE__ */ jsx(
|
|
634
|
+
Captions,
|
|
635
|
+
{
|
|
636
|
+
className: cn(
|
|
637
|
+
"absolute inset-x-0 bottom-8 z-10 flex flex-col items-center select-none break-words text-center transition-[bottom] duration-200 ease-in-out group-data-[started]:group-hover:bottom-[72px] media-preview:opacity-0 aria-hidden:invisible aria-hidden:opacity-0 [&_[data-part=cue]]:inline [&_[data-part=cue]]:rounded-md [&_[data-part=cue]]:bg-black/95 [&_[data-part=cue]]:px-4 [&_[data-part=cue]]:py-1.5 [&_[data-part=cue]]:text-[15px] [&_[data-part=cue]]:leading-snug [&_[data-part=cue]]:text-white [&_[data-part=cue]]:drop-shadow-[0_1px_3px_rgba(0,0,0,0.8)]",
|
|
638
|
+
chromeVisible && "bottom-[72px]"
|
|
639
|
+
)
|
|
640
|
+
}
|
|
641
|
+
);
|
|
642
|
+
}
|
|
643
|
+
function DesktopControls({
|
|
644
|
+
hideWhenPaused,
|
|
645
|
+
children
|
|
646
|
+
}) {
|
|
647
|
+
const chromeVisible = usePausedChromeVisible(hideWhenPaused);
|
|
648
|
+
return /* @__PURE__ */ jsx(
|
|
649
|
+
Controls.Root,
|
|
650
|
+
{
|
|
651
|
+
className: cn(
|
|
652
|
+
"absolute inset-0 z-20 flex h-full w-full flex-col bg-gradient-to-t from-black/60 via-transparent to-transparent opacity-0 transition-opacity duration-200 group-data-[started]:group-hover:opacity-100",
|
|
653
|
+
chromeVisible && "opacity-100"
|
|
654
|
+
),
|
|
655
|
+
children
|
|
656
|
+
}
|
|
657
|
+
);
|
|
658
|
+
}
|
|
564
659
|
|
|
565
660
|
export { VideoPlayer };
|
|
566
|
-
//# sourceMappingURL=chunk-
|
|
567
|
-
//# sourceMappingURL=chunk-
|
|
661
|
+
//# sourceMappingURL=chunk-CDHGWZID.js.map
|
|
662
|
+
//# sourceMappingURL=chunk-CDHGWZID.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/ui/video-player.tsx"],"names":["forwardRef","CaptionsIcon","VideoPlayer","_a","onEnded"],"mappings":"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","file":"chunk-CDHGWZID.js","sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport {\n MediaPlayer,\n MediaProvider,\n Poster,\n Controls,\n Gesture,\n TimeSlider,\n VolumeSlider,\n Time,\n PlayButton,\n MuteButton,\n FullscreenButton,\n CaptionButton,\n PIPButton,\n SeekButton,\n Captions,\n Spinner,\n Track,\n Tooltip,\n Menu,\n LiveButton,\n MediaAnnouncer,\n useMediaState,\n useMediaRemote,\n useVideoQualityOptions,\n useCaptionOptions,\n type MediaPlayerInstance,\n type MediaPlayerProps,\n type PlayerSrc,\n} from \"@vidstack/react\"\n// Desktop-only controls use the above. Mobile uses DefaultVideoLayout below.\nimport {\n DefaultVideoLayout,\n defaultLayoutIcons,\n} from \"@vidstack/react/player/layouts/default\"\nimport \"@vidstack/react/player/styles/base.css\"\nimport \"@vidstack/react/player/styles/default/theme.css\"\nimport \"@vidstack/react/player/styles/default/layouts/video.css\"\nimport {\n Play,\n Pause,\n Volume2,\n VolumeX,\n Volume1,\n Maximize,\n Minimize,\n Captions as CaptionsIcon,\n CaptionsOff,\n PictureInPicture2,\n RotateCcw,\n RotateCw,\n Gauge,\n Settings,\n Check,\n} from \"lucide-react\"\nimport { CycleIcon } from \"@/components/icons\"\nimport { cn } from \"@/lib/utils\"\nimport {\n type PlayerLocale,\n type VideoDesktopLabels,\n resolveVideoLabels,\n resolveVidstackTranslations,\n} from \"@/components/ui/player-i18n\"\n\n/* ─── Types ─── */\n\nexport interface CaptionTrack {\n /** URL do arquivo VTT de legenda */\n src: string\n /** Codigo do idioma (BCP 47, ex: \"pt-BR\", \"en\", \"es\") */\n language: string\n /** Label exibido no seletor de legendas (ex: \"Portugues\", \"English\") */\n label: string\n /** Se true, esta faixa sera ativada por padrao */\n default?: boolean\n}\n\nexport interface VideoPlayerRef {\n play(): void\n pause(): void\n readonly currentTime: number\n readonly duration: number\n}\n\n/** @deprecated Use `Partial<VideoDesktopLabels>` — kept for backward compatibility */\nexport type VideoPlayerLabels = Partial<VideoDesktopLabels>\n\n/** Fonte de video: URL string ou objeto com MIME type explicito.\n O Vidstack infere o tipo pela EXTENSAO no path da URL — quando ela nao\n existe (blob: de arquivo local, endpoints sem extensao), o provider\n nao carrega. Nesses casos, passar { src, type } (ex: type do File.type\n local ou o ContentType salvo no upload). */\nexport type VideoPlayerSrc = string | { src: string; type: string }\n\n/** blob: → \"video/object\" (unica rota que o isVideoSrc do Vidstack aceita\n para blob). Demais objetos passam direto — cast porque o runtime aceita\n mais MIME types que a uniao VideoMimeType dos tipos. */\nfunction resolveVideoSrc(src: VideoPlayerSrc): PlayerSrc {\n if (typeof src === \"string\") return src\n if (src.src.startsWith(\"blob:\")) return { src: src.src, type: \"video/object\" }\n return src as PlayerSrc\n}\n\n/** Espaco reservado nos lados do player em fullscreen (valores CSS: \"22rem\", \"40dvh\"...). */\nexport interface VideoPlayerInset {\n top?: string\n right?: string\n bottom?: string\n left?: string\n}\n\nconst INSET_SIDES = [\"top\", \"right\", \"bottom\", \"left\"] as const\n\n/** Vira `--cycle-video-fs-<lado>` no root do player. Lidas pelo chrome desktop\n (wrapper) e pelo CSS do layout mobile (tokens.css) — so em [data-fullscreen]. */\nfunction insetVars(inset: VideoPlayerInset | undefined): MediaPlayerProps[\"style\"] {\n if (!inset) return undefined\n const vars: Record<`--${string}`, string> = {}\n for (const side of INSET_SIDES) {\n if (inset[side]) vars[`--cycle-video-fs-${side}`] = inset[side] as string\n }\n return vars\n}\n\nexport interface VideoPlayerProps {\n /** Fonte do video: URL (mp4, webm, m3u8 para HLS) ou { src, type } para blob:/URL sem extensao */\n src: VideoPlayerSrc\n /** URL da imagem de poster */\n poster?: string\n /** Alt text do poster */\n posterAlt?: string\n /** URL do arquivo de thumbnails (VTT sprite sheet) */\n thumbnails?: string\n /** URL do arquivo VTT de chapters (marcadores de seção no timeline) */\n chapters?: string\n /** Faixas de legenda/subtitles (VTT). Suporta multiplos idiomas. */\n captions?: CaptionTrack[]\n /** Exibir spinner durante buffering (default: true) */\n showBuffering?: boolean\n /** Habilitar anuncios de acessibilidade para screen readers (default: true) */\n announcer?: boolean\n /** Iniciar automaticamente */\n autoPlay?: boolean\n /** Iniciar mutado */\n muted?: boolean\n /** Repetir ao terminar */\n loop?: boolean\n /** Tempo inicial em segundos — usado para retomar de onde o usuario parou (resume position) */\n startTime?: number\n /** Idioma dos controles (default: \"pt-BR\"). 13 locales pre-configurados. */\n locale?: PlayerLocale\n /** Overrides pontuais de labels — aplicados em cima do locale selecionado */\n labels?: VideoPlayerLabels\n /**\n * Esconde overlay de play, escurecimento e barra de controles quando o video e pausado\n * depois de iniciado — para o consumidor colocar conteudo proprio sobre o frame (ex.: pop-quiz).\n * Play inicial e replay ao terminar continuam aparecendo. Controles voltam no hover\n * (desktop) ou toque (mobile) e somem sozinhos. (default: false)\n */\n hideControlsWhenPaused?: boolean\n /**\n * Reserva espaco nos lados do player quando ELE entra em fullscreen (botao/duplo clique),\n * para o consumidor renderizar conteudo proprio ali (ex.: coluna de pergunta a direita em\n * paisagem, abaixo do video em pe). O video encolhe e a barra de controles, overlay, legenda\n * e gestos recuam junto — nada fica por baixo do conteudo. Fora do fullscreen nao muda nada.\n * Ex.: `{ right: \"22rem\" }` ou `{ bottom: \"40dvh\" }`.\n */\n fullscreenInset?: VideoPlayerInset\n /** Callback com progresso do video (currentTime em segundos, duration em segundos) */\n onTimeUpdate?: (currentTime: number, duration: number) => void\n /** Callback disparado quando o video termina */\n onEnded?: () => void\n className?: string\n}\n\n/* ─── fullscreenInset (classes) ─── */\n\n/** Padding do root so em fullscreen, lendo as variaveis do inset (fallback 0 = sem efeito). */\nconst FS_INSET_PADDING =\n \"data-[fullscreen]:pt-[var(--cycle-video-fs-top,0px)] data-[fullscreen]:pr-[var(--cycle-video-fs-right,0px)] data-[fullscreen]:pb-[var(--cycle-video-fs-bottom,0px)] data-[fullscreen]:pl-[var(--cycle-video-fs-left,0px)]\"\n\n/** Wrapper do chrome desktop: cobre o player e, em fullscreen, recua pelo inset.\n Sem z-index/opacity/transform de proposito — nao cria stacking context, entao os\n z-index dos filhos (overlay 10, controles 20, feedbacks 35) seguem valendo contra o root. */\nconst FS_INSET_CHROME =\n \"absolute inset-0 group-data-[fullscreen]:top-[var(--cycle-video-fs-top,0px)] group-data-[fullscreen]:right-[var(--cycle-video-fs-right,0px)] group-data-[fullscreen]:bottom-[var(--cycle-video-fs-bottom,0px)] group-data-[fullscreen]:left-[var(--cycle-video-fs-left,0px)]\"\n\n/* ─── Labels context ─── */\n\nconst VideoLabelsContext = React.createContext<VideoDesktopLabels>(\n resolveVideoLabels(\"pt-BR\")\n)\nfunction useVideoLabels() {\n return React.useContext(VideoLabelsContext)\n}\n\n/* ─── Shared styles ─── */\n\nconst controlBtnClass =\n \"group inline-flex size-9 cursor-pointer items-center justify-center rounded-md text-white outline-none transition-colors hover:bg-white/20 focus-visible:ring-2 focus-visible:ring-white/50\"\n\n/* SEM `animate-in`/`animate-out` aqui — de propósito.\n O Popper do Vidstack esconde o tooltip assim: `if (hasAnimation(content))`\n (= `getComputedStyle(el).animationName !== \"none\"`) ele NÃO aplica\n `display: none` na hora; espera um evento `animationend`. As utilities\n `animate-in` deixam `animation-name: enter` permanente no elemento, e o\n par `data-[state=closed]:animate-out` é convenção do Radix — o Vidstack\n marca `data-visible`, nunca `data-state`. Resultado: depois que a animação\n de entrada termina, nenhum `animationend` novo dispara, o `display: none`\n nunca chega e o tooltip fica congelado na tela (ClickUp 86ajtpear).\n Transition não entra no `animationName`, então o hide volta a ser síncrono. */\nconst tooltipClass =\n \"z-50 rounded bg-black/90 px-2 py-1 text-xs font-medium text-white shadow-lg transition-opacity duration-150\"\n\n/** Wraps a control button with a Vidstack tooltip. Desktop only (no hover on mobile). */\nfunction ControlTooltip({ label, children }: { label: string; children: React.ReactNode }) {\n return (\n <Tooltip.Root>\n <Tooltip.Trigger asChild>{children}</Tooltip.Trigger>\n <Tooltip.Content className={tooltipClass} placement=\"top\">\n {label}\n </Tooltip.Content>\n </Tooltip.Root>\n )\n}\n\n/** Play, Pause e Volume usam icones filled (solid) no player — igual YouTube */\nconst filledIconClass = \"text-white fill-current !stroke-transparent\"\n\n/* ─── Sub-components (controles customizados) ─── */\n\n/** Se o \"chrome\" de pause (overlay, gradiente, barra) deve ficar visivel agora.\n Sem `hideWhenPaused` e o comportamento classico: visivel sempre que pausado.\n Com a prop, pausa depois de iniciado fica limpa — so o play inicial e o replay\n ao terminar mantem o chrome. */\nfunction usePausedChromeVisible(hideWhenPaused: boolean) {\n const isPaused = useMediaState(\"paused\")\n const hasStarted = useMediaState(\"started\")\n const hasEnded = useMediaState(\"ended\")\n return isPaused && (!hideWhenPaused || !hasStarted || hasEnded)\n}\n\nfunction PlayOverlay({ hideWhenPaused }: { hideWhenPaused: boolean }) {\n const visible = usePausedChromeVisible(hideWhenPaused)\n if (!visible) return null\n\n return (\n <PlayButton\n data-slot=\"video-play-overlay\"\n className=\"absolute inset-0 z-10 flex cursor-pointer items-center justify-center bg-black/30 transition-opacity\"\n >\n <div className=\"flex size-16 items-center justify-center rounded-full bg-white/20 backdrop-blur-sm transition-transform hover:scale-110\">\n <CycleIcon icon={Play} size=\"lg\" decorative className={cn(filledIconClass, \"ml-1\")} />\n </div>\n </PlayButton>\n )\n}\n\nfunction PlayControl() {\n const isPaused = useMediaState(\"paused\")\n const l = useVideoLabels()\n return (\n <ControlTooltip label={isPaused ? l.play : l.pause}>\n <PlayButton className={controlBtnClass}>\n {isPaused ? (\n <CycleIcon icon={Play} size=\"sm\" decorative className={filledIconClass} />\n ) : (\n <CycleIcon icon={Pause} size=\"sm\" decorative className={filledIconClass} />\n )}\n </PlayButton>\n </ControlTooltip>\n )\n}\n\nfunction SeekBackwardControl() {\n const l = useVideoLabels()\n return (\n <ControlTooltip label={l.seekBackward}>\n <SeekButton className={controlBtnClass} seconds={-10}>\n <CycleIcon icon={RotateCcw} size=\"sm\" decorative className=\"text-white\" />\n </SeekButton>\n </ControlTooltip>\n )\n}\n\nfunction SeekForwardControl() {\n const l = useVideoLabels()\n return (\n <ControlTooltip label={l.seekForward}>\n <SeekButton className={controlBtnClass} seconds={10}>\n <CycleIcon icon={RotateCw} size=\"sm\" decorative className=\"text-white\" />\n </SeekButton>\n </ControlTooltip>\n )\n}\n\nfunction MuteControl() {\n const volume = useMediaState(\"volume\")\n const isMuted = useMediaState(\"muted\")\n const l = useVideoLabels()\n\n const Icon = isMuted || volume === 0 ? VolumeX : volume < 0.5 ? Volume1 : Volume2\n const label = isMuted || volume === 0 ? l.unmute : l.mute\n\n return (\n <ControlTooltip label={label}>\n <MuteButton className={controlBtnClass}>\n <CycleIcon icon={Icon} size=\"sm\" decorative className={filledIconClass} />\n </MuteButton>\n </ControlTooltip>\n )\n}\n\nfunction VolumeControl() {\n return (\n <VolumeSlider.Root className=\"group relative hidden h-9 w-20 shrink-0 cursor-pointer touch-none select-none items-center outline-none @[480px]:inline-flex\">\n <VolumeSlider.Track className=\"relative h-[4px] w-full rounded-full bg-white/30\">\n <VolumeSlider.TrackFill className=\"absolute h-full w-[var(--slider-fill)] rounded-full bg-white\" />\n </VolumeSlider.Track>\n <VolumeSlider.Thumb className=\"absolute left-[var(--slider-fill)] top-1/2 size-3 -translate-x-1/2 -translate-y-1/2 rounded-full bg-white opacity-0 transition-opacity group-data-[active]:opacity-100 group-data-[dragging]:opacity-100\" />\n </VolumeSlider.Root>\n )\n}\n\nfunction BufferingIndicator() {\n return (\n <div className=\"pointer-events-none absolute inset-0 z-[35] flex items-center justify-center media-buffering:opacity-100 media-can-play:opacity-0 opacity-0 transition-opacity duration-300\">\n <Spinner.Root className=\"size-16 text-white\" size={64}>\n <Spinner.Track className=\"opacity-25\" width={4} />\n <Spinner.TrackFill className=\"opacity-75\" width={4} />\n </Spinner.Root>\n </div>\n )\n}\n\nfunction SeekBar({ thumbnails, chapters }: { thumbnails?: string; chapters?: boolean }) {\n const trackContent = (\n <>\n <TimeSlider.Track className=\"relative h-[4px] w-full rounded-full bg-white/30\">\n <TimeSlider.Progress className=\"absolute h-full w-[var(--slider-progress)] rounded-full bg-white/50\" />\n <TimeSlider.TrackFill className=\"absolute h-full w-[var(--slider-fill)] rounded-full bg-white\" />\n </TimeSlider.Track>\n </>\n )\n\n return (\n <TimeSlider.Root className=\"group relative inline-flex h-9 w-full cursor-pointer touch-none select-none items-center outline-none\">\n {chapters ? (\n <TimeSlider.Chapters className=\"relative flex w-full items-center\">\n {(cues, forwardRef) =>\n cues.map((cue) => (\n <div\n className=\"relative flex h-full w-full items-center last:mr-0 mr-0.5\"\n key={cue.startTime}\n ref={forwardRef}\n >\n {trackContent}\n </div>\n ))\n }\n </TimeSlider.Chapters>\n ) : (\n trackContent\n )}\n\n <TimeSlider.Thumb className=\"absolute left-[var(--slider-fill)] top-1/2 size-3 -translate-x-1/2 -translate-y-1/2 rounded-full bg-white opacity-0 transition-opacity group-data-[active]:opacity-100 group-data-[dragging]:opacity-100\" />\n\n {/* Thumbnail preview on hover */}\n {thumbnails && (\n <TimeSlider.Preview className=\"pointer-events-none flex flex-col items-center gap-1 opacity-0 transition-opacity duration-200 data-[visible]:opacity-100\">\n <TimeSlider.Thumbnail.Root\n src={thumbnails}\n className=\"block h-[var(--thumbnail-height)] max-h-[160px] min-h-[80px] w-[var(--thumbnail-width)] min-w-[120px] max-w-[200px] overflow-hidden rounded-md border-2 border-white bg-black\"\n >\n <TimeSlider.Thumbnail.Img />\n </TimeSlider.Thumbnail.Root>\n {chapters && <TimeSlider.ChapterTitle className=\"text-[11px] text-white/80\" />}\n <TimeSlider.Value className=\"rounded bg-black/80 px-1.5 py-0.5 text-[11px] font-mono text-white\" />\n </TimeSlider.Preview>\n )}\n\n {/* Time preview (no thumbnails) */}\n {!thumbnails && (\n <TimeSlider.Preview className=\"pointer-events-none flex flex-col items-center opacity-0 transition-opacity duration-200 data-[visible]:opacity-100\">\n {chapters && <TimeSlider.ChapterTitle className=\"text-[11px] text-white/80 mb-0.5\" />}\n <TimeSlider.Value className=\"rounded bg-black/80 px-1.5 py-0.5 text-[11px] font-mono text-white\" />\n </TimeSlider.Preview>\n )}\n </TimeSlider.Root>\n )\n}\n\nfunction TimeDisplay() {\n return (\n <div className=\"flex shrink-0 items-center gap-1 text-xs font-mono text-white/80 tabular-nums whitespace-nowrap\">\n <Time type=\"current\" />\n <span>/</span>\n <Time type=\"duration\" />\n </div>\n )\n}\n\nfunction CaptionControl() {\n const track = useMediaState(\"textTrack\")\n const hasCaptions = useMediaState(\"hasCaptions\")\n const options = useCaptionOptions()\n const l = useVideoLabels()\n\n // Single track or none: simple toggle button (native CaptionButton behavior)\n if (options.length <= 2) {\n return (\n <ControlTooltip label={track ? l.captionsOn : l.captionsOff}>\n <CaptionButton className={cn(controlBtnClass, !hasCaptions && \"opacity-50 pointer-events-none\")}>\n {track ? (\n <CycleIcon icon={CaptionsIcon} size=\"sm\" decorative className=\"text-white\" />\n ) : (\n <CycleIcon icon={CaptionsOff} size=\"sm\" decorative className=\"text-white\" />\n )}\n </CaptionButton>\n </ControlTooltip>\n )\n }\n\n // Multiple tracks: menu with language selection (native Vidstack Menu behavior)\n return (\n <Menu.Root>\n <ControlTooltip label={l.captions}>\n <Menu.Button className={controlBtnClass}>\n {track ? (\n <CycleIcon icon={CaptionsIcon} size=\"sm\" decorative className=\"text-white\" />\n ) : (\n <CycleIcon icon={CaptionsOff} size=\"sm\" decorative className=\"text-white\" />\n )}\n </Menu.Button>\n </ControlTooltip>\n\n <Menu.Content\n className=\"absolute bottom-full right-0 z-50 mb-2 min-w-[140px] rounded-lg border border-white/10 bg-black/90 backdrop-blur-md p-1 shadow-xl\"\n placement=\"top\"\n >\n <p className=\"px-3 py-1 text-[11px] font-semibold uppercase tracking-wider text-white/50\">\n {l.captions}\n </p>\n <Menu.RadioGroup value={options.selectedValue}>\n {options.map((option) => (\n <Menu.Radio\n key={option.value}\n value={option.value}\n onSelect={option.select}\n className={cn(\n \"flex w-full items-center gap-2 rounded-md px-3 py-1.5 text-sm text-white/80 transition-colors hover:bg-white/10 hover:text-white cursor-pointer\",\n option.selected && \"text-white font-medium\"\n )}\n >\n <span className=\"size-4 flex items-center justify-center\">\n {option.selected && <CycleIcon icon={Check} size=\"2xs\" decorative className=\"text-white\" />}\n </span>\n <span>{option.label}</span>\n </Menu.Radio>\n ))}\n </Menu.RadioGroup>\n </Menu.Content>\n </Menu.Root>\n )\n}\n\nfunction PIPControl() {\n const isPIP = useMediaState(\"pictureInPicture\")\n const canPIP = useMediaState(\"canPictureInPicture\")\n const l = useVideoLabels()\n\n if (!canPIP) return null\n\n return (\n <ControlTooltip label={isPIP ? l.pipExit : l.pip}>\n <PIPButton className={controlBtnClass}>\n <CycleIcon icon={PictureInPicture2} size=\"sm\" decorative className={cn(\"text-white\", isPIP && \"text-white/100\")} />\n </PIPButton>\n </ControlTooltip>\n )\n}\n\nfunction SpeedControl() {\n const playbackRate = useMediaState(\"playbackRate\")\n const remote = useMediaRemote()\n const [open, setOpen] = React.useState(false)\n const l = useVideoLabels()\n\n const rates = [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2]\n\n return (\n <div className=\"relative\">\n <ControlTooltip label={l.speed}>\n <button\n type=\"button\"\n onClick={() => setOpen(!open)}\n className={controlBtnClass}\n >\n {playbackRate === 1 ? (\n <CycleIcon icon={Gauge} size=\"sm\" decorative className=\"text-white\" />\n ) : (\n <span className=\"text-xs font-mono font-semibold text-white\">{playbackRate}x</span>\n )}\n </button>\n </ControlTooltip>\n\n {open && (\n <>\n {/* Backdrop to close */}\n <div className=\"fixed inset-0 z-40\" onClick={() => setOpen(false)} />\n\n <div className=\"absolute bottom-full right-0 z-50 mb-2 min-w-[120px] rounded-lg border border-white/10 bg-black/90 backdrop-blur-md p-1 shadow-xl\">\n {rates.map((rate) => (\n <button\n key={rate}\n type=\"button\"\n onClick={() => {\n remote.changePlaybackRate(rate)\n setOpen(false)\n }}\n className={cn(\n \"flex w-full items-center gap-2 rounded-md px-3 py-1.5 text-sm text-white/80 transition-colors hover:bg-white/10 hover:text-white\",\n playbackRate === rate && \"text-white font-medium\"\n )}\n >\n <span className=\"size-4 flex items-center justify-center\">\n {playbackRate === rate && <CycleIcon icon={Check} size=\"2xs\" decorative className=\"text-white\" />}\n </span>\n <span className=\"font-mono\">{rate === 1 ? l.speedNormal : `${rate}x`}</span>\n </button>\n ))}\n </div>\n </>\n )}\n </div>\n )\n}\n\nfunction QualityControl() {\n const options = useVideoQualityOptions({ auto: true, sort: \"descending\" })\n const [open, setOpen] = React.useState(false)\n const l = useVideoLabels()\n\n const currentLabel = options.selectedValue === \"auto\"\n ? `Auto${options.selectedQuality ? ` (${options.selectedQuality.height}p)` : \"\"}`\n : options.selectedQuality\n ? `${options.selectedQuality.height}p`\n : \"Auto\"\n\n if (options.disabled) return null\n\n return (\n <div className=\"relative\">\n <ControlTooltip label={l.quality}>\n <button\n type=\"button\"\n onClick={() => setOpen(!open)}\n className={controlBtnClass}\n >\n <CycleIcon icon={Settings} size=\"sm\" decorative className=\"text-white\" />\n </button>\n </ControlTooltip>\n\n {open && (\n <>\n <div className=\"fixed inset-0 z-40\" onClick={() => setOpen(false)} />\n\n <div className=\"absolute bottom-full right-0 z-50 mb-2 min-w-[140px] rounded-lg border border-white/10 bg-black/90 backdrop-blur-md p-1 shadow-xl\">\n <p className=\"px-3 py-1 text-[11px] font-semibold uppercase tracking-wider text-white/50\">\n {l.qualityMenu}\n </p>\n {options.map((option) => (\n <button\n key={option.value}\n type=\"button\"\n onClick={() => {\n option.select()\n setOpen(false)\n }}\n className={cn(\n \"flex w-full items-center gap-2 rounded-md px-3 py-1.5 text-sm text-white/80 transition-colors hover:bg-white/10 hover:text-white\",\n option.selected && \"text-white font-medium\"\n )}\n >\n <span className=\"size-4 flex items-center justify-center\">\n {option.selected && <CycleIcon icon={Check} size=\"2xs\" decorative className=\"text-white\" />}\n </span>\n <span className=\"font-mono\">{option.label}</span>\n </button>\n ))}\n </div>\n </>\n )}\n </div>\n )\n}\n\nfunction FullscreenControl() {\n const isFullscreen = useMediaState(\"fullscreen\")\n const l = useVideoLabels()\n return (\n <ControlTooltip label={isFullscreen ? l.fullscreenExit : l.fullscreen}>\n <FullscreenButton className={controlBtnClass}>\n {isFullscreen ? (\n <CycleIcon icon={Minimize} size=\"sm\" decorative className=\"text-white\" />\n ) : (\n <CycleIcon icon={Maximize} size=\"sm\" decorative className=\"text-white\" />\n )}\n </FullscreenButton>\n </ControlTooltip>\n )\n}\n\nfunction LiveIndicator() {\n const isLive = useMediaState(\"live\")\n const isLiveEdge = useMediaState(\"liveEdge\")\n const l = useVideoLabels()\n\n if (!isLive) return null\n\n return (\n <LiveButton\n className={cn(\n \"inline-flex cursor-pointer items-center gap-1.5 rounded-md px-2 py-1 text-xs font-semibold uppercase tracking-wider transition-colors\",\n isLiveEdge\n ? \"text-destructive\"\n : \"text-white/60 hover:text-white\"\n )}\n >\n <span\n className={cn(\n \"size-2 rounded-full\",\n isLiveEdge ? \"bg-destructive animate-pulse\" : \"bg-white/40\"\n )}\n />\n {l.live}\n </LiveButton>\n )\n}\n\n\n/* ─── Gesture helpers ─── */\n\ntype SeekDirection = \"backward\" | \"forward\"\n\nconst DOUBLE_TAP_THRESHOLD = 300\n\n/** Double-tap left/right edges to seek ±10s. z-10 so it doesn't block controls (z-20). */\nfunction SeekGestureZone({\n side,\n onSeekFeedback,\n}: {\n side: \"left\" | \"right\"\n onSeekFeedback: (direction: SeekDirection) => void\n}) {\n const remote = useMediaRemote()\n const currentTime = useMediaState(\"currentTime\")\n const lastTapRef = React.useRef(0)\n\n const handlePointerUp = React.useCallback(\n (e: React.PointerEvent) => {\n const now = Date.now()\n const delta = now - lastTapRef.current\n lastTapRef.current = now\n\n if (delta < DOUBLE_TAP_THRESHOLD) {\n e.stopPropagation()\n const seconds = side === \"left\" ? -10 : 10\n remote.seek(currentTime + seconds)\n onSeekFeedback(side === \"left\" ? \"backward\" : \"forward\")\n }\n },\n [remote, currentTime, side, onSeekFeedback]\n )\n\n return (\n <div\n className={cn(\n \"absolute top-0 z-10 block h-full w-1/5\",\n side === \"left\" ? \"left-0\" : \"right-0\"\n )}\n onPointerUp={handlePointerUp}\n />\n )\n}\n\nfunction SeekFeedbackOverlay({\n direction,\n visible,\n seconds,\n}: {\n direction: SeekDirection\n visible: boolean\n seconds: number\n}) {\n const isBackward = direction === \"backward\"\n\n return (\n <div\n className={cn(\n \"pointer-events-none absolute top-0 z-[35] flex h-full w-2/5 items-center transition-opacity duration-200\",\n isBackward ? \"left-0 justify-center\" : \"right-0 justify-center\",\n visible ? \"opacity-100\" : \"opacity-0\"\n )}\n >\n <div\n className={cn(\n \"flex flex-col items-center gap-1 transition-transform duration-300\",\n visible ? \"scale-100\" : \"scale-75\"\n )}\n >\n <div className=\"flex size-12 items-center justify-center rounded-full bg-white/20 backdrop-blur-sm\">\n <CycleIcon\n icon={isBackward ? RotateCcw : RotateCw}\n size=\"sm\"\n decorative\n className=\"text-white\"\n />\n </div>\n <span className=\"text-xs font-semibold text-white drop-shadow-md\">\n {seconds}s\n </span>\n </div>\n </div>\n )\n}\n\n\n/* ─── Hook: detect desktop via pointer capability ─── */\n/* Uses pointer: fine (mouse/trackpad) instead of viewport width.\n This way mobile devices keep the touch-optimized DefaultVideoLayout\n even when fullscreen in landscape exceeds the sm breakpoint. */\n\nfunction useIsDesktop() {\n const [isDesktop, setIsDesktop] = React.useState(false)\n\n React.useEffect(() => {\n const mq = window.matchMedia(\"(pointer: fine)\")\n setIsDesktop(mq.matches)\n const handler = (e: MediaQueryListEvent) => setIsDesktop(e.matches)\n mq.addEventListener(\"change\", handler)\n return () => mq.removeEventListener(\"change\", handler)\n }, [])\n\n return isDesktop\n}\n\n/* ─── Main component ─── */\n\nexport const VideoPlayer = React.forwardRef<VideoPlayerRef, VideoPlayerProps>(\n function VideoPlayer(\n {\n src,\n poster,\n posterAlt = \"\",\n thumbnails,\n chapters,\n captions,\n showBuffering = true,\n announcer = true,\n autoPlay = false,\n muted = false,\n loop = false,\n startTime,\n locale = \"pt-BR\",\n labels,\n hideControlsWhenPaused = false,\n fullscreenInset,\n onTimeUpdate,\n onEnded,\n className,\n },\n ref\n ) {\n const resolvedLabels = React.useMemo(\n () => resolveVideoLabels(locale, labels),\n [locale, labels]\n )\n const vidstackTranslations = React.useMemo(\n () => resolveVidstackTranslations(locale, labels),\n [locale, labels]\n )\n const player = React.useRef<MediaPlayerInstance>(null)\n const resolvedSrc = React.useMemo(() => resolveVideoSrc(src), [src])\n\n React.useImperativeHandle(ref, () => ({\n play() {\n player.current?.play()\n },\n pause() {\n player.current?.pause()\n },\n get currentTime() {\n return player.current?.currentTime ?? 0\n },\n get duration() {\n return player.current?.duration ?? 0\n },\n }))\n\n const handleTimeUpdate = React.useCallback(\n (detail: { currentTime: number }) => {\n if (onTimeUpdate && player.current) {\n onTimeUpdate(detail.currentTime, player.current.duration)\n }\n },\n [onTimeUpdate]\n )\n\n const handleEnded = React.useCallback(() => {\n onEnded?.()\n }, [onEnded])\n\n // Resume position: seek to startTime once the player is ready\n const hasAppliedStartTime = React.useRef(false)\n const handleCanPlay = React.useCallback(() => {\n if (startTime && startTime > 0 && !hasAppliedStartTime.current && player.current) {\n player.current.currentTime = startTime\n hasAppliedStartTime.current = true\n }\n }, [startTime])\n\n const playerEvents = {\n onCanPlay: handleCanPlay,\n ...(onTimeUpdate ? { onTimeUpdate: handleTimeUpdate } : {}),\n ...(onEnded ? { onEnded: handleEnded } : {}),\n }\n const isDesktop = useIsDesktop()\n const fsInsetStyle = React.useMemo(() => insetVars(fullscreenInset), [fullscreenInset])\n\n // Seek feedback state with accumulation (like YouTube)\n const [seekFeedback, setSeekFeedback] = React.useState<SeekDirection | null>(null)\n const [seekAccumulated, setSeekAccumulated] = React.useState(0)\n const feedbackTimeout = React.useRef<ReturnType<typeof setTimeout>>(undefined)\n\n const handleSeekFeedback = React.useCallback((direction: SeekDirection) => {\n clearTimeout(feedbackTimeout.current)\n\n setSeekFeedback((prev) => {\n if (prev === direction) {\n setSeekAccumulated((s) => s + 10)\n } else {\n setSeekAccumulated(10)\n }\n return direction\n })\n\n feedbackTimeout.current = setTimeout(() => {\n setSeekFeedback(null)\n setSeekAccumulated(0)\n }, 700)\n }, [])\n\n // Mobile: listen for seek requests from DefaultVideoLayout gestures\n React.useEffect(() => {\n const el = player.current?.el\n if (!el || isDesktop) return\n\n const handler = (e: Event) => {\n const seekTo = (e as CustomEvent<number>).detail\n const current = player.current?.currentTime ?? 0\n const delta = seekTo - current\n // Only show feedback for gesture seeks (~±10s), not slider scrubs\n if (Math.abs(Math.abs(delta) - 10) < 2) {\n handleSeekFeedback(delta < 0 ? \"backward\" : \"forward\")\n }\n }\n\n el.addEventListener(\"media-seek-request\", handler)\n return () => el.removeEventListener(\"media-seek-request\", handler)\n }, [isDesktop, handleSeekFeedback])\n\n React.useEffect(() => {\n return () => clearTimeout(feedbackTimeout.current)\n }, [])\n\n // Mobile + hideControlsWhenPaused: o DefaultVideoLayout e do Vidstack, e o\n // MediaControls dele mostra os controles ao pausar (show(0)) e desliga o\n // rastreamento de ociosidade enquanto pausado — nenhuma prop muda isso\n // (controlsDelay so vale tocando; hideControlsOnMouseLeave so reage a mouse).\n // Entao: escondemos via API publica `controls.hide(0)` logo apos o pause e\n // armamos o idle nos mesmos quando o toque (gesto toggle:controls) os traz de\n // volta. Menu aberto/slider arrastando chamam pauseControls, e hide() vira\n // no-op nesse caso — sem conflito. Ao terminar, mostramos de novo (replay).\n React.useEffect(() => {\n const p = player.current\n const el = p?.el\n if (!p || !el || isDesktop || !hideControlsWhenPaused) return\n\n let timer: ReturnType<typeof setTimeout> | undefined\n const clear = () => clearTimeout(timer)\n const quietPause = () => p.state.started && p.state.paused && !p.state.ended\n const hide = () => {\n clear()\n if (quietPause()) p.controls.hide(0)\n }\n const arm = () => {\n clear()\n if (quietPause()) timer = setTimeout(hide, p.controls.defaultDelay)\n }\n // Microtask: o listener de pause do Vidstack (show) ja rodou quando este dispara.\n const onPause = () => queueMicrotask(hide)\n const onControlsChange = (e: Event) => {\n if ((e as CustomEvent<boolean>).detail) arm()\n else clear()\n }\n const onEnded = () => {\n clear()\n p.controls.show(0)\n }\n\n el.addEventListener(\"pause\", onPause)\n el.addEventListener(\"ended\", onEnded)\n el.addEventListener(\"controls-change\", onControlsChange)\n el.addEventListener(\"media-resume-controls-request\", arm)\n return () => {\n clear()\n el.removeEventListener(\"pause\", onPause)\n el.removeEventListener(\"ended\", onEnded)\n el.removeEventListener(\"controls-change\", onControlsChange)\n el.removeEventListener(\"media-resume-controls-request\", arm)\n }\n }, [isDesktop, hideControlsWhenPaused])\n\n return (\n <VideoLabelsContext.Provider value={resolvedLabels}>\n <MediaPlayer\n ref={player}\n src={resolvedSrc}\n autoPlay={autoPlay}\n muted={muted}\n loop={loop}\n playsInline\n crossOrigin=\"\"\n style={fsInsetStyle}\n className={cn(\n \"group relative aspect-video w-full overflow-hidden bg-black text-white\",\n isDesktop ? \"rounded-[16px]\" : \"rounded-none\",\n // fullscreenInset: o proprio player reserva o espaco (padding) — o provider de\n // midia e relative/100%, entao o video encolhe junto. O chrome recua via wrapper\n // (desktop) ou CSS em tokens.css (mobile), ambos lendo as mesmas variaveis.\n FS_INSET_PADDING,\n className\n )}\n {...playerEvents}\n >\n <MediaProvider>\n {poster && (\n <Poster\n className={cn(\"absolute inset-0 block h-full w-full object-cover opacity-0 transition-opacity data-[visible]:opacity-100\", isDesktop && \"rounded-[16px]\")}\n src={poster}\n alt={posterAlt}\n />\n )}\n\n {/* ─── Chapters track (VTT) ─── */}\n {chapters && (\n <Track\n src={chapters}\n kind=\"chapters\"\n language=\"en\"\n default\n />\n )}\n\n {/* ─── Caption/subtitle tracks (VTT) ─── */}\n {captions?.map((track) => (\n <Track\n key={track.language}\n src={track.src}\n kind=\"subtitles\"\n language={track.language}\n label={track.label}\n default={track.default}\n />\n ))}\n </MediaProvider>\n\n {/* ─── Accessibility: screen reader announcements ─── */}\n {announcer && <MediaAnnouncer />}\n\n {/* ─── Buffering spinner ─── */}\n {showBuffering && <BufferingIndicator />}\n\n {/* ─── Seek feedback indicators (both mobile & desktop) ─── */}\n <SeekFeedbackOverlay direction=\"backward\" visible={seekFeedback === \"backward\"} seconds={seekAccumulated} />\n <SeekFeedbackOverlay direction=\"forward\" visible={seekFeedback === \"forward\"} seconds={seekAccumulated} />\n\n {/* ─── MOBILE: Vidstack DefaultVideoLayout (touch, gestos, menus nativos) ─── */}\n {/* O layout default do Vidstack traz affordances que não fazem sentido num\n player de aula e que chegaram como bug do QA (ClickUp 86ajtqjc9):\n - noAudioGain: o submenu \"Áudio\" só tinha o slider \"Amplificar 300%\".\n Sem o slider e com uma única faixa de áudio, o DefaultAudioMenu\n retorna null e o submenu inteiro desaparece.\n - noKeyboardAnimations: o toggle \"Animações de teclado\" é config de\n demo, não de aluno.\n - googleCastButton: null — o Cast nunca completa (precisa de receiver\n app id + mídia pública); um botão que não funciona é pior que\n nenhum. AirPlay segue (o Safari resolve nativamente).\n O item \"Avisos\" do menu Acessibilidade não tem prop no Vidstack — é\n escondido por CSS em `styles/tokens.css`. */}\n {!isDesktop && (\n <DefaultVideoLayout\n icons={defaultLayoutIcons}\n thumbnails={thumbnails}\n colorScheme=\"dark\"\n translations={vidstackTranslations}\n noAudioGain\n noKeyboardAnimations\n slots={{ googleCastButton: null }}\n />\n )}\n\n {/* ─── DESKTOP: Controles customizados do Cycle Design ─── */}\n {isDesktop && (\n <div data-slot=\"video-chrome\" className={FS_INSET_CHROME}>\n <DesktopCaptions hideWhenPaused={hideControlsWhenPaused} />\n\n {/* Play overlay (paused state — big play button) */}\n <PlayOverlay hideWhenPaused={hideControlsWhenPaused} />\n\n {/* Gestures: click to pause, double-click fullscreen */}\n <Gesture className=\"absolute inset-0 z-0 block h-full w-full\" event=\"pointerup\" action=\"toggle:paused\" />\n <Gesture className=\"absolute inset-0 z-0 block h-full w-full\" event=\"dblpointerup\" action=\"toggle:fullscreen\" />\n\n {/* Double-tap seek zones on edges */}\n <SeekGestureZone side=\"left\" onSeekFeedback={handleSeekFeedback} />\n <SeekGestureZone side=\"right\" onSeekFeedback={handleSeekFeedback} />\n\n {/* Controls overlay (hover to show) */}\n <DesktopControls hideWhenPaused={hideControlsWhenPaused}>\n <div className=\"flex-1\" />\n\n {/* Seek bar */}\n <Controls.Group className=\"flex w-full items-center px-3\">\n <SeekBar thumbnails={thumbnails} chapters={!!chapters} />\n </Controls.Group>\n\n {/* Bottom bar */}\n <Controls.Group className=\"@container flex w-full items-center gap-1 px-2 pb-2\">\n <PlayControl />\n <SeekBackwardControl />\n <SeekForwardControl />\n <MuteControl />\n <VolumeControl />\n <TimeDisplay />\n <LiveIndicator />\n\n <div className=\"flex-1\" />\n\n <CaptionControl />\n <SpeedControl />\n <QualityControl />\n <PIPControl />\n <FullscreenControl />\n </Controls.Group>\n </DesktopControls>\n </div>\n )}\n </MediaPlayer>\n </VideoLabelsContext.Provider>\n )\n }\n)\n\n/* ─── Desktop chrome (legenda + barra), sensivel ao modo de pausa ─── */\n\n/** Captions overlay.\n `aria-hidden:*` é obrigatório: ao desligar a legenda o <Captions>\n do Vidstack só marca aria-hidden=\"true\" e para de atualizar o\n renderer — quem apaga a caixa é a folha\n `@vidstack/react/player/styles/default/captions.css`\n (`opacity: 0; visibility: hidden`), que este layout não usa\n porque estiliza o overlay com classes próprias. Sem isso o último\n cue fica congelado na tela até o fim do vídeo (ClickUp 86ajtpear).\n O estilo do cue usa combinador DESCENDENTE (`[&_[data-part=cue]]`),\n não filho: a lib media-captions insere `[data-part=cue-display]`\n como filho direto do overlay e o `[data-part=cue]` fica um nível\n abaixo (issue #20 — com `>` as classes nunca casavam e o visual\n vinha dos defaults do Vidstack por acidente).\n A legenda sobe 72px quando a barra esta visivel (hover ou chrome de pause). */\nfunction DesktopCaptions({ hideWhenPaused }: { hideWhenPaused: boolean }) {\n const chromeVisible = usePausedChromeVisible(hideWhenPaused)\n return (\n <Captions\n className={cn(\n \"absolute inset-x-0 bottom-8 z-10 flex flex-col items-center select-none break-words text-center transition-[bottom] duration-200 ease-in-out group-data-[started]:group-hover:bottom-[72px] media-preview:opacity-0 aria-hidden:invisible aria-hidden:opacity-0 [&_[data-part=cue]]:inline [&_[data-part=cue]]:rounded-md [&_[data-part=cue]]:bg-black/95 [&_[data-part=cue]]:px-4 [&_[data-part=cue]]:py-1.5 [&_[data-part=cue]]:text-[15px] [&_[data-part=cue]]:leading-snug [&_[data-part=cue]]:text-white [&_[data-part=cue]]:drop-shadow-[0_1px_3px_rgba(0,0,0,0.8)]\",\n chromeVisible && \"bottom-[72px]\"\n )}\n />\n )\n}\n\n/** Barra de controles desktop: aparece no hover depois de iniciado e fica fixa\n enquanto o chrome de pause esta visivel (ver usePausedChromeVisible). */\nfunction DesktopControls({\n hideWhenPaused,\n children,\n}: {\n hideWhenPaused: boolean\n children: React.ReactNode\n}) {\n const chromeVisible = usePausedChromeVisible(hideWhenPaused)\n return (\n <Controls.Root\n className={cn(\n \"absolute inset-0 z-20 flex h-full w-full flex-col bg-gradient-to-t from-black/60 via-transparent to-transparent opacity-0 transition-opacity duration-200 group-data-[started]:group-hover:opacity-100\",\n chromeVisible && \"opacity-100\"\n )}\n >\n {children}\n </Controls.Root>\n )\n}\n\n"]}
|