@fluencypassdevs/cycle 1.6.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/bin/mcp.mjs +150 -0
  2. package/dist/chunk-37C2K2NM.js +101 -0
  3. package/dist/chunk-37C2K2NM.js.map +1 -0
  4. package/dist/chunk-I5A3CME4.js +84 -0
  5. package/dist/chunk-I5A3CME4.js.map +1 -0
  6. package/dist/chunk-JRJBKPXW.js +118 -0
  7. package/dist/chunk-JRJBKPXW.js.map +1 -0
  8. package/dist/{chunk-PCLVQPIG.js → chunk-L2DJS6PG.js} +3 -3
  9. package/dist/{chunk-PCLVQPIG.js.map → chunk-L2DJS6PG.js.map} +1 -1
  10. package/dist/chunk-PW464XEP.js +787 -0
  11. package/dist/chunk-PW464XEP.js.map +1 -0
  12. package/dist/chunk-QANROH2K.js +50 -0
  13. package/dist/chunk-QANROH2K.js.map +1 -0
  14. package/dist/chunk-YMWRR7ET.js +503 -0
  15. package/dist/chunk-YMWRR7ET.js.map +1 -0
  16. package/dist/index.d.ts +8 -1
  17. package/dist/index.js +18 -12
  18. package/dist/layout/container.d.ts +24 -0
  19. package/dist/layout/container.js +5 -0
  20. package/dist/layout/container.js.map +1 -0
  21. package/dist/layout/grid.d.ts +26 -0
  22. package/dist/layout/grid.js +5 -0
  23. package/dist/layout/grid.js.map +1 -0
  24. package/dist/layout/stack.d.ts +47 -0
  25. package/dist/layout/stack.js +5 -0
  26. package/dist/layout/stack.js.map +1 -0
  27. package/dist/types-CVdl5nka.d.ts +9 -0
  28. package/dist/ui/chat-message.d.ts +57 -0
  29. package/dist/ui/chat-message.js +11 -0
  30. package/dist/ui/chat-message.js.map +1 -0
  31. package/dist/ui/chat-panel.js +1 -1
  32. package/dist/ui/live-waiting.js +2 -2
  33. package/dist/ui/message-bar.d.ts +69 -0
  34. package/dist/ui/message-bar.js +5 -0
  35. package/dist/ui/message-bar.js.map +1 -0
  36. package/dist/ui/message-rating.d.ts +29 -0
  37. package/dist/ui/message-rating.js +5 -0
  38. package/dist/ui/message-rating.js.map +1 -0
  39. package/package.json +1 -1
package/bin/mcp.mjs CHANGED
@@ -266,6 +266,86 @@ const COMPONENTS = [
266
266
  <CircularProgress value={50} showLabel={false} />`,
267
267
  keywords: ["progress", "circular", "circle", "anel", "ring", "porcentagem", "percentage", "score", "nota", "aproveitamento"],
268
268
  },
269
+ {
270
+ name: "Container",
271
+ import: `import { Container } from "@fluencypassdevs/cycle"`,
272
+ description: "Layout primitive: wrapper centralizado com max-width e margin LOCKED do design system (16/24px). Use sempre como wrapper de paginas/secoes. Margin custom (ex: 40px desktop) via className override.",
273
+ props: [
274
+ { name: "size", type: '"prose" | "default" | "wide" | "full"', default: '"default"' },
275
+ { name: "padding", type: '"default" | "none"', default: '"default"' },
276
+ { name: "as", type: '"div" | "section" | "main" | "article"', default: '"div"' },
277
+ ],
278
+ example: `<Container>conteudo principal (max 1380px, margin 16/24)</Container>
279
+ <Container size="prose">leitura/forms (max 860px)</Container>
280
+ <Container padding="none">hero full-bleed</Container>
281
+
282
+ {/* Override custom (raro) */}
283
+ <Container className="sm:px-10">conteudo com mais respiro (40px desktop)</Container>`,
284
+ keywords: ["container", "wrapper", "max-width", "centralizar", "layout", "secao", "pagina", "margin"],
285
+ },
286
+ {
287
+ name: "Stack",
288
+ import: `import { Stack } from "@fluencypassdevs/cycle"`,
289
+ description: "Layout primitive: flex com gap, direction e alinhamento. Use em vez de flex flex-col gap-X manual. Gap tokens xs=4 sm=8 md=12 lg=16 xl=24 px.",
290
+ props: [
291
+ { name: "direction", type: '"col" | "row"', default: '"col"' },
292
+ { name: "directionSm", type: '"col" | "row"', default: "-" },
293
+ { name: "directionLg", type: '"col" | "row"', default: "-" },
294
+ { name: "gap", type: '"xs" | "sm" | "md" | "lg" | "xl"', default: '"lg"' },
295
+ { name: "gapSm", type: '"xs" | "sm" | "md" | "lg" | "xl"', default: "-" },
296
+ { name: "align", type: '"start" | "center" | "end" | "stretch"', default: "-" },
297
+ { name: "justify", type: '"start" | "center" | "end" | "between"', default: "-" },
298
+ ],
299
+ example: `<Stack gap="xl">vertical</Stack>
300
+ <Stack direction="col" directionSm="row" justify="between">action bar responsivo</Stack>
301
+ <Stack gap="md" gapSm="xl">gap responsivo</Stack>`,
302
+ keywords: ["stack", "flex", "vstack", "hstack", "vertical", "horizontal", "gap", "espacamento"],
303
+ },
304
+ {
305
+ name: "Grid",
306
+ import: `import { Grid, GridItem } from "@fluencypassdevs/cycle"`,
307
+ description: "Layout primitive: CSS grid totalmente prescrito. 12 colunas FIXAS, gutter LOCKED (16px mobile, 24px desktop). Items usam GridItem com size 1-12 para ocupar fracoes. Sem props pra cols/gap — sistema decide tudo. Apenas minItemWidth para galerias auto-fit.",
308
+ props: [
309
+ { name: "minItemWidth", type: "string", default: "-" },
310
+ ],
311
+ example: `{/* 3 cards iguais (1 mobile, 3 desktop) */}
312
+ <Grid>
313
+ <GridItem size={12} sizeMd={4}>Card</GridItem>
314
+ <GridItem size={12} sizeMd={4}>Card</GridItem>
315
+ <GridItem size={12} sizeMd={4}>Card</GridItem>
316
+ </Grid>
317
+
318
+ {/* Hero + 2 metades */}
319
+ <Grid>
320
+ <GridItem size={12}>Hero</GridItem>
321
+ <GridItem size={12} sizeMd={6}>Card A</GridItem>
322
+ <GridItem size={12} sizeMd={6}>Card B</GridItem>
323
+ </Grid>
324
+
325
+ {/* Galeria auto-fit */}
326
+ <Grid minItemWidth="280px">cards</Grid>`,
327
+ keywords: ["grid", "colunas", "cols", "responsive", "layout", "cards", "12-col"],
328
+ },
329
+ {
330
+ name: "GridItem",
331
+ import: `import { GridItem } from "@fluencypassdevs/cycle"`,
332
+ description: "Filho do Grid. Define quantas das 12 colunas ocupar em cada breakpoint via size/sizeSm/sizeMd/sizeLg. Pense em fracoes: size=12 (full), size=6 (1/2), size=4 (1/3), size=3 (1/4).",
333
+ props: [
334
+ { name: "size", type: "1 a 12", default: "-" },
335
+ { name: "sizeSm", type: "1 a 12", default: "-" },
336
+ { name: "sizeMd", type: "1 a 12", default: "-" },
337
+ { name: "sizeLg", type: "1 a 12", default: "-" },
338
+ ],
339
+ example: `{/* Full no mobile, metade no desktop */}
340
+ <GridItem size={12} sizeMd={6}>Card</GridItem>
341
+
342
+ {/* Sempre um terco */}
343
+ <GridItem size={4}>1/3</GridItem>
344
+
345
+ {/* Sidebar 1/4 desktop */}
346
+ <GridItem size={12} sizeMd={3}>Sidebar</GridItem>`,
347
+ keywords: ["grid", "item", "size", "col-span", "colunas", "child", "fracao"],
348
+ },
269
349
  {
270
350
  name: "FileCard",
271
351
  import: `import { FileCard } from "@fluencypassdevs/cycle"`,
@@ -328,6 +408,76 @@ const COMPONENTS = [
328
408
  example: `<ChatPanel messages={messages} onSend={handleSend} currentUser="aluno@email.com" />`,
329
409
  keywords: ["chat", "painel", "conversa", "mensagens", "live", "tempo real"],
330
410
  },
411
+ {
412
+ name: "ChatMessage",
413
+ import: `import { ChatMessage } from "@fluencypassdevs/cycle"`,
414
+ description: "Mensagem de conversa com IA. Persona ai/user/system, suporta texto + audio inline + like/dislike. Loading state com typing indicator. AI usa icone Ai + bubble transparente. User usa bubble pink coral (theme-brand) + avatar.",
415
+ props: [
416
+ { name: "persona", type: '"ai" | "user" | "system"', default: "-" },
417
+ { name: "text", type: "string", default: "-" },
418
+ { name: "audioSrc", type: "string", default: "-" },
419
+ { name: "avatar", type: "string", default: "-" },
420
+ { name: "author", type: "string", default: "-" },
421
+ { name: "showFeedback", type: "boolean", default: "false" },
422
+ { name: "feedbackValue", type: '"like" | "dislike" | null', default: "-" },
423
+ { name: "onFeedbackChange", type: "(val) => void", default: "-" },
424
+ { name: "loading", type: "boolean", default: "false" },
425
+ ],
426
+ example: `<ChatMessage persona="ai" text="Como posso ajudar?" />
427
+ <ChatMessage persona="ai" text="Vou responder..." audioSrc="/audio.mp3" showFeedback />
428
+ <ChatMessage persona="user" text="Oi" avatar="/me.jpg" author="Felipe" />
429
+ <ChatMessage persona="ai" loading />
430
+ <ChatMessage persona="system" text="Conversa iniciada" />`,
431
+ keywords: ["chat", "ai", "ia", "mensagem", "conversa", "message", "assistant", "bot"],
432
+ },
433
+ {
434
+ name: "MessageRating",
435
+ import: `import { MessageRating } from "@fluencypassdevs/cycle"`,
436
+ description: "Rating de 5 niveis com emoji usado ao final de uma conversa. Niveis: Disappointing/Frustrating/Ok/Helpful/Loved it. Layout horizontal (desktop) ou vertical (mobile). Labels customizaveis para i18n.",
437
+ props: [
438
+ { name: "value", type: "1 | 2 | 3 | 4 | 5 | null", default: "-" },
439
+ { name: "defaultValue", type: "1 | 2 | 3 | 4 | 5 | null", default: "-" },
440
+ { name: "onChange", type: "(val) => void", default: "-" },
441
+ { name: "layout", type: '"horizontal" | "vertical" | "auto"', default: '"auto"' },
442
+ { name: "labels", type: "Partial<MessageRatingLabels>", default: "-" },
443
+ { name: "disabled", type: "boolean", default: "false" },
444
+ { name: "title", type: "string", default: "-" },
445
+ ],
446
+ example: `<MessageRating onChange={(v) => save(v)} title="Como foi sua experiencia?" />
447
+ <MessageRating value={3} disabled />`,
448
+ keywords: ["rating", "emoji", "avaliacao", "feedback", "experiencia", "score", "satisfacao"],
449
+ },
450
+ {
451
+ name: "MessageBar",
452
+ import: `import { MessageBar } from "@fluencypassdevs/cycle"`,
453
+ description: "Input bar para chat com IA. 6 estados: default, audio-only, active, disabled, recording, paused. UI-only: consumer implementa MediaRecorder + HTMLAudioElement via callbacks. Live waveform real durante recording (Web Audio API, rolling window estilo WhatsApp). RecordedWaveform real durante paused com peaks + barras tocadas/nao-tocadas + dot verde + click/drag-to-seek. Press-to-record no mobile (touch-only): long-press inicia, drag-up trava em hands-free, drag-left cancela, release < 1s cancela, release >= 1s envia. Vibracao haptica em lock/cancel.",
454
+ props: [
455
+ { name: "state", type: '"default" | "audio-only" | "active" | "disabled" | "recording" | "paused"', default: '"default"' },
456
+ { name: "value", type: "string", default: '""' },
457
+ { name: "onChange", type: "(val) => void", default: "-" },
458
+ { name: "onSendText", type: "(text) => void", default: "-" },
459
+ { name: "onStartRecording", type: "() => void", default: "-" },
460
+ { name: "onPauseRecording", type: "() => void", default: "-" },
461
+ { name: "onResumeRecording", type: "() => void", default: "-" },
462
+ { name: "onCancelRecording", type: "() => void", default: "-" },
463
+ { name: "onSendAudio", type: "() => void", default: "-" },
464
+ { name: "onTogglePlay", type: "() => void", default: "-" },
465
+ { name: "recordingDuration", type: "number", default: "0" },
466
+ { name: "isPlaying", type: "boolean", default: "false" },
467
+ { name: "recordingStream", type: "MediaStream | null", default: "null", note: "passe o MESMO stream do MediaRecorder; o componente analisa em tempo real para renderizar o live waveform" },
468
+ { name: "playbackProgress", type: "number (0-1)", default: "0", note: "consumer atualiza via audio.ontimeupdate enquanto toca o preview no estado paused" },
469
+ { name: "onSeekPlayback", type: "(progress) => void", default: "-", note: "consumer seta audio.currentTime = progress * duration" },
470
+ { name: "placeholder", type: "string", default: '"Digite sua mensagem..."' },
471
+ ],
472
+ example: `// 1) Captura: passa o MESMO stream pro MessageBar (live waveform).
473
+ // 2) Pause: recorder.stop() gera o Blob; crie um <audio> e atualize playbackProgress via ontimeupdate.
474
+ // 3) Cleanup: revokeObjectURL ao cancelar/enviar.
475
+
476
+ <MessageBar value={text} onChange={setText} onSendText={(t) => send(t)} onStartRecording={() => startRec()} />
477
+ <MessageBar state="recording" recordingDuration={4} recordingStream={stream} onPauseRecording={pause} onCancelRecording={cancel} onSendAudio={send} />
478
+ <MessageBar state="paused" recordingDuration={42} isPlaying={isPlaying} playbackProgress={progress} onTogglePlay={togglePlay} onSeekPlayback={seek} onResumeRecording={resume} onCancelRecording={cancel} onSendAudio={send} />`,
479
+ keywords: ["chat", "input", "message", "bar", "audio", "recording", "microphone", "mensagem", "envio", "press-to-record", "waveform", "playback", "whatsapp"],
480
+ },
331
481
  {
332
482
  name: "LikeDislike",
333
483
  import: `import { LikeDislike } from "@fluencypassdevs/cycle"`,
@@ -0,0 +1,101 @@
1
+ import { cn } from './chunk-TYCPXAXF.js';
2
+ import { __objRest, __spreadValues, __spreadProps } from './chunk-YINJ5YZ5.js';
3
+ import * as React from 'react';
4
+ import { jsxs, jsx } from 'react/jsx-runtime';
5
+
6
+ var DEFAULT_LABELS = {
7
+ 1: { emoji: "\u{1F621}", title: "Disappointing", subtitle: "Decepcionante" },
8
+ 2: { emoji: "\u{1F61E}", title: "Frustrating", subtitle: "Frustrante" },
9
+ 3: { emoji: "\u{1F610}", title: "Ok", subtitle: "Tudo bem" },
10
+ 4: { emoji: "\u{1F642}", title: "Helpful", subtitle: "Ajudou" },
11
+ 5: { emoji: "\u{1F60D}", title: "Loved it", subtitle: "Amei" }
12
+ };
13
+ var VALUES = [1, 2, 3, 4, 5];
14
+ function MessageRating(_a) {
15
+ var _b = _a, {
16
+ value: controlledValue,
17
+ defaultValue,
18
+ onChange,
19
+ layout = "auto",
20
+ labels: labelOverrides,
21
+ disabled = false,
22
+ title,
23
+ className
24
+ } = _b, props = __objRest(_b, [
25
+ "value",
26
+ "defaultValue",
27
+ "onChange",
28
+ "layout",
29
+ "labels",
30
+ "disabled",
31
+ "title",
32
+ "className"
33
+ ]);
34
+ const [internalValue, setInternalValue] = React.useState(
35
+ defaultValue != null ? defaultValue : null
36
+ );
37
+ const isControlled = controlledValue !== void 0;
38
+ const value = isControlled ? controlledValue : internalValue;
39
+ const labels = React.useMemo(
40
+ () => __spreadValues(__spreadValues({}, DEFAULT_LABELS), labelOverrides),
41
+ [labelOverrides]
42
+ );
43
+ const handleSelect = (v) => {
44
+ if (disabled) return;
45
+ if (!isControlled) setInternalValue(v);
46
+ onChange == null ? void 0 : onChange(v);
47
+ };
48
+ const layoutClass = layout === "vertical" ? "flex-col" : "flex-row";
49
+ return /* @__PURE__ */ jsxs(
50
+ "div",
51
+ __spreadProps(__spreadValues({
52
+ "data-slot": "message-rating",
53
+ role: "radiogroup",
54
+ "aria-label": title != null ? title : "Avalie sua experiencia",
55
+ className: cn("flex flex-col gap-3", className)
56
+ }, props), {
57
+ children: [
58
+ title && /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-foreground", children: title }),
59
+ /* @__PURE__ */ jsx("div", { className: cn("flex gap-2", layoutClass), children: VALUES.map((v) => {
60
+ const label = labels[v];
61
+ const selected = value === v;
62
+ return /* @__PURE__ */ jsxs(
63
+ "button",
64
+ {
65
+ type: "button",
66
+ role: "radio",
67
+ "aria-checked": selected,
68
+ "aria-label": `${label.title} \u2014 ${label.subtitle}`,
69
+ disabled,
70
+ onClick: () => handleSelect(v),
71
+ className: cn(
72
+ /* Base — shared mobile/desktop */
73
+ "flex items-center transition-[color,background-color,border-color,box-shadow] outline-none",
74
+ "hover:opacity-90 focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
75
+ "disabled:cursor-not-allowed disabled:opacity-50",
76
+ /* Mobile: compacto, justa o emoji centralizado em pill cinza */
77
+ "rounded-xl px-3 py-2 justify-center",
78
+ /* Desktop: card completo com texto, flex-1, border */
79
+ "sm:flex-1 sm:gap-3 sm:px-3 sm:py-2.5 sm:text-left sm:justify-start sm:border",
80
+ /* Selected vs unselected (estados diferentes por breakpoint) */
81
+ selected ? "bg-accent theme-brand sm:border-primary sm:shadow-xs" : "bg-muted sm:bg-transparent sm:border-neutral-border"
82
+ ),
83
+ children: [
84
+ /* @__PURE__ */ jsx("span", { className: "text-base sm:text-2xl leading-none shrink-0", "aria-hidden": "true", children: label.emoji }),
85
+ /* @__PURE__ */ jsxs("span", { className: "hidden sm:flex sm:flex-col min-w-0", children: [
86
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium leading-tight text-foreground truncate", children: label.title }),
87
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground leading-tight truncate", children: label.subtitle })
88
+ ] })
89
+ ]
90
+ },
91
+ v
92
+ );
93
+ }) })
94
+ ]
95
+ })
96
+ );
97
+ }
98
+
99
+ export { MessageRating };
100
+ //# sourceMappingURL=chunk-37C2K2NM.js.map
101
+ //# sourceMappingURL=chunk-37C2K2NM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/ui/message-rating.tsx"],"names":[],"mappings":"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","file":"chunk-37C2K2NM.js","sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\n/* ─── Types ───────────────────────────────────────────────────── */\n\nexport type MessageRatingValue = 1 | 2 | 3 | 4 | 5\n\nexport interface RatingLabel {\n emoji: string\n title: string\n subtitle: string\n}\n\nexport type MessageRatingLabels = Record<MessageRatingValue, RatingLabel>\n\nexport interface MessageRatingProps extends Omit<React.ComponentProps<\"div\">, \"onChange\" | \"defaultValue\"> {\n /** Valor selecionado (controlado) */\n value?: MessageRatingValue | null\n /** Valor inicial (nao controlado) */\n defaultValue?: MessageRatingValue | null\n /** Callback ao escolher uma opcao */\n onChange?: (value: MessageRatingValue) => void\n /** Layout. \"auto\" usa vertical (column) abaixo de sm e horizontal acima */\n layout?: \"horizontal\" | \"vertical\" | \"auto\"\n /** Labels customizadas (default: PT-BR) */\n labels?: Partial<MessageRatingLabels>\n /** Desabilita a interacao */\n disabled?: boolean\n /** Titulo opcional acima das opcoes */\n title?: string\n}\n\n/* ─── Default labels (PT-BR) ──────────────────────────────────── */\n\nconst DEFAULT_LABELS: MessageRatingLabels = {\n 1: { emoji: \"😡\", title: \"Disappointing\", subtitle: \"Decepcionante\" },\n 2: { emoji: \"😞\", title: \"Frustrating\", subtitle: \"Frustrante\" },\n 3: { emoji: \"😐\", title: \"Ok\", subtitle: \"Tudo bem\" },\n 4: { emoji: \"🙂\", title: \"Helpful\", subtitle: \"Ajudou\" },\n 5: { emoji: \"😍\", title: \"Loved it\", subtitle: \"Amei\" },\n}\n\nconst VALUES: MessageRatingValue[] = [1, 2, 3, 4, 5]\n\n/* ─── Component ───────────────────────────────────────────────── */\n\nfunction MessageRating({\n value: controlledValue,\n defaultValue,\n onChange,\n layout = \"auto\",\n labels: labelOverrides,\n disabled = false,\n title,\n className,\n ...props\n}: MessageRatingProps) {\n const [internalValue, setInternalValue] = React.useState<MessageRatingValue | null>(\n defaultValue ?? null\n )\n const isControlled = controlledValue !== undefined\n const value = isControlled ? controlledValue : internalValue\n\n const labels: MessageRatingLabels = React.useMemo(\n () => ({ ...DEFAULT_LABELS, ...labelOverrides }),\n [labelOverrides]\n )\n\n const handleSelect = (v: MessageRatingValue) => {\n if (disabled) return\n if (!isControlled) setInternalValue(v)\n onChange?.(v)\n }\n\n /* Layout: vertical (column) sobrescreve so essa direcao. horizontal/auto\n sao iguais agora (sempre flex-row, pois mobile e desktop diferem no\n conteudo de cada botao, nao na direcao do container). */\n const layoutClass = layout === \"vertical\" ? \"flex-col\" : \"flex-row\"\n\n return (\n <div\n data-slot=\"message-rating\"\n role=\"radiogroup\"\n aria-label={title ?? \"Avalie sua experiencia\"}\n className={cn(\"flex flex-col gap-3\", className)}\n {...props}\n >\n {title && (\n <p className=\"text-sm font-medium text-foreground\">{title}</p>\n )}\n <div className={cn(\"flex gap-2\", layoutClass)}>\n {VALUES.map((v) => {\n const label = labels[v]\n const selected = value === v\n return (\n <button\n key={v}\n type=\"button\"\n role=\"radio\"\n aria-checked={selected}\n aria-label={`${label.title} — ${label.subtitle}`}\n disabled={disabled}\n onClick={() => handleSelect(v)}\n className={cn(\n /* Base — shared mobile/desktop */\n \"flex items-center transition-[color,background-color,border-color,box-shadow] outline-none\",\n \"hover:opacity-90 focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50\",\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n /* Mobile: compacto, justa o emoji centralizado em pill cinza */\n \"rounded-xl px-3 py-2 justify-center\",\n /* Desktop: card completo com texto, flex-1, border */\n \"sm:flex-1 sm:gap-3 sm:px-3 sm:py-2.5 sm:text-left sm:justify-start sm:border\",\n /* Selected vs unselected (estados diferentes por breakpoint) */\n selected\n ? \"bg-accent theme-brand sm:border-primary sm:shadow-xs\"\n : \"bg-muted sm:bg-transparent sm:border-neutral-border\"\n )}\n >\n <span className=\"text-base sm:text-2xl leading-none shrink-0\" aria-hidden=\"true\">\n {label.emoji}\n </span>\n {/* Labels: visiveis apenas no desktop */}\n <span className=\"hidden sm:flex sm:flex-col min-w-0\">\n <span className=\"text-sm font-medium leading-tight text-foreground truncate\">\n {label.title}\n </span>\n <span className=\"text-xs text-muted-foreground leading-tight truncate\">\n {label.subtitle}\n </span>\n </span>\n </button>\n )\n })}\n </div>\n </div>\n )\n}\n\nexport { MessageRating }\n"]}
@@ -0,0 +1,84 @@
1
+ import { cn } from './chunk-TYCPXAXF.js';
2
+ import { __objRest, __spreadValues } from './chunk-YINJ5YZ5.js';
3
+ import { jsx } from 'react/jsx-runtime';
4
+
5
+ var DIRECTION = {
6
+ col: "flex-col",
7
+ row: "flex-row"
8
+ };
9
+ var DIRECTION_SM = {
10
+ col: "sm:flex-col",
11
+ row: "sm:flex-row"
12
+ };
13
+ var DIRECTION_LG = {
14
+ col: "lg:flex-col",
15
+ row: "lg:flex-row"
16
+ };
17
+ var GAP = {
18
+ xs: "gap-1",
19
+ sm: "gap-2",
20
+ md: "gap-3",
21
+ lg: "gap-4",
22
+ xl: "gap-6"
23
+ };
24
+ var GAP_SM = {
25
+ xs: "sm:gap-1",
26
+ sm: "sm:gap-2",
27
+ md: "sm:gap-3",
28
+ lg: "sm:gap-4",
29
+ xl: "sm:gap-6"
30
+ };
31
+ var ALIGN = {
32
+ start: "items-start",
33
+ center: "items-center",
34
+ end: "items-end",
35
+ stretch: "items-stretch"
36
+ };
37
+ var JUSTIFY = {
38
+ start: "justify-start",
39
+ center: "justify-center",
40
+ end: "justify-end",
41
+ between: "justify-between"
42
+ };
43
+ function Stack(_a) {
44
+ var _b = _a, {
45
+ direction = "col",
46
+ directionSm,
47
+ directionLg,
48
+ gap = "lg",
49
+ gapSm,
50
+ align,
51
+ justify,
52
+ className
53
+ } = _b, props = __objRest(_b, [
54
+ "direction",
55
+ "directionSm",
56
+ "directionLg",
57
+ "gap",
58
+ "gapSm",
59
+ "align",
60
+ "justify",
61
+ "className"
62
+ ]);
63
+ return /* @__PURE__ */ jsx(
64
+ "div",
65
+ __spreadValues({
66
+ "data-slot": "stack",
67
+ className: cn(
68
+ "flex",
69
+ DIRECTION[direction],
70
+ directionSm && DIRECTION_SM[directionSm],
71
+ directionLg && DIRECTION_LG[directionLg],
72
+ GAP[gap],
73
+ gapSm && GAP_SM[gapSm],
74
+ align && ALIGN[align],
75
+ justify && JUSTIFY[justify],
76
+ className
77
+ )
78
+ }, props)
79
+ );
80
+ }
81
+
82
+ export { Stack };
83
+ //# sourceMappingURL=chunk-I5A3CME4.js.map
84
+ //# sourceMappingURL=chunk-I5A3CME4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/layout/stack.tsx"],"names":[],"mappings":";;;;AAKA,IAAM,SAAA,GAAY;AAAA,EAChB,GAAA,EAAK,UAAA;AAAA,EACL,GAAA,EAAK;AACP,CAAA;AAEA,IAAM,YAAA,GAAe;AAAA,EACnB,GAAA,EAAK,aAAA;AAAA,EACL,GAAA,EAAK;AACP,CAAA;AAEA,IAAM,YAAA,GAAe;AAAA,EACnB,GAAA,EAAK,aAAA;AAAA,EACL,GAAA,EAAK;AACP,CAAA;AAEA,IAAM,GAAA,GAAgC;AAAA,EACpC,EAAA,EAAI,OAAA;AAAA,EACJ,EAAA,EAAI,OAAA;AAAA,EACJ,EAAA,EAAI,OAAA;AAAA,EACJ,EAAA,EAAI,OAAA;AAAA,EACJ,EAAA,EAAI;AACN,CAAA;AAEA,IAAM,MAAA,GAAmC;AAAA,EACvC,EAAA,EAAI,UAAA;AAAA,EACJ,EAAA,EAAI,UAAA;AAAA,EACJ,EAAA,EAAI,UAAA;AAAA,EACJ,EAAA,EAAI,UAAA;AAAA,EACJ,EAAA,EAAI;AACN,CAAA;AAEA,IAAM,KAAA,GAAQ;AAAA,EACZ,KAAA,EAAO,aAAA;AAAA,EACP,MAAA,EAAQ,cAAA;AAAA,EACR,GAAA,EAAK,WAAA;AAAA,EACL,OAAA,EAAS;AACX,CAAA;AAEA,IAAM,OAAA,GAAU;AAAA,EACd,KAAA,EAAO,eAAA;AAAA,EACP,MAAA,EAAQ,gBAAA;AAAA,EACR,GAAA,EAAK,aAAA;AAAA,EACL,OAAA,EAAS;AACX,CAAA;AAmBA,SAAS,MAAM,EAAA,EAUA;AAVA,EAAA,IAAA,EAAA,GAAA,EAAA,EACb;AAAA,IAAA,SAAA,GAAY,KAAA;AAAA,IACZ,WAAA;AAAA,IACA,WAAA;AAAA,IACA,GAAA,GAAM,IAAA;AAAA,IACN,KAAA;AAAA,IACA,KAAA;AAAA,IACA,OAAA;AAAA,IACA;AAAA,GA3EF,GAmEe,EAAA,EASV,KAAA,GAAA,SAAA,CATU,EAAA,EASV;AAAA,IARH,WAAA;AAAA,IACA,aAAA;AAAA,IACA,aAAA;AAAA,IACA,KAAA;AAAA,IACA,OAAA;AAAA,IACA,OAAA;AAAA,IACA,SAAA;AAAA,IACA;AAAA,GAAA,CAAA;AAGA,EAAA,uBACE,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA,cAAA,CAAA;AAAA,MACC,WAAA,EAAU,OAAA;AAAA,MACV,SAAA,EAAW,EAAA;AAAA,QACT,MAAA;AAAA,QACA,UAAU,SAAS,CAAA;AAAA,QACnB,WAAA,IAAe,aAAa,WAAW,CAAA;AAAA,QACvC,WAAA,IAAe,aAAa,WAAW,CAAA;AAAA,QACvC,IAAI,GAAG,CAAA;AAAA,QACP,KAAA,IAAS,OAAO,KAAK,CAAA;AAAA,QACrB,KAAA,IAAS,MAAM,KAAK,CAAA;AAAA,QACpB,OAAA,IAAW,QAAQ,OAAO,CAAA;AAAA,QAC1B;AAAA;AACF,KAAA,EACI,KAAA;AAAA,GACN;AAEJ","file":"chunk-I5A3CME4.js","sourcesContent":["import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\nimport type { GapToken } from \"./types\"\n\nconst DIRECTION = {\n col: \"flex-col\",\n row: \"flex-row\",\n} as const\n\nconst DIRECTION_SM = {\n col: \"sm:flex-col\",\n row: \"sm:flex-row\",\n} as const\n\nconst DIRECTION_LG = {\n col: \"lg:flex-col\",\n row: \"lg:flex-row\",\n} as const\n\nconst GAP: Record<GapToken, string> = {\n xs: \"gap-1\",\n sm: \"gap-2\",\n md: \"gap-3\",\n lg: \"gap-4\",\n xl: \"gap-6\",\n}\n\nconst GAP_SM: Record<GapToken, string> = {\n xs: \"sm:gap-1\",\n sm: \"sm:gap-2\",\n md: \"sm:gap-3\",\n lg: \"sm:gap-4\",\n xl: \"sm:gap-6\",\n}\n\nconst ALIGN = {\n start: \"items-start\",\n center: \"items-center\",\n end: \"items-end\",\n stretch: \"items-stretch\",\n} as const\n\nconst JUSTIFY = {\n start: \"justify-start\",\n center: \"justify-center\",\n end: \"justify-end\",\n between: \"justify-between\",\n} as const\n\nexport interface StackProps extends React.ComponentProps<\"div\"> {\n /** Flex direction (default: col) */\n direction?: keyof typeof DIRECTION\n /** Direction at sm breakpoint (>= 640px) */\n directionSm?: keyof typeof DIRECTION_SM\n /** Direction at lg breakpoint (>= 1024px) */\n directionLg?: keyof typeof DIRECTION_LG\n /** Gap between items (default: lg = 16px) */\n gap?: GapToken\n /** Gap at sm breakpoint */\n gapSm?: GapToken\n /** Cross-axis alignment */\n align?: keyof typeof ALIGN\n /** Main-axis alignment */\n justify?: keyof typeof JUSTIFY\n}\n\nfunction Stack({\n direction = \"col\",\n directionSm,\n directionLg,\n gap = \"lg\",\n gapSm,\n align,\n justify,\n className,\n ...props\n}: StackProps) {\n return (\n <div\n data-slot=\"stack\"\n className={cn(\n \"flex\",\n DIRECTION[direction],\n directionSm && DIRECTION_SM[directionSm],\n directionLg && DIRECTION_LG[directionLg],\n GAP[gap],\n gapSm && GAP_SM[gapSm],\n align && ALIGN[align],\n justify && JUSTIFY[justify],\n className\n )}\n {...props}\n />\n )\n}\n\nexport { Stack }\n"]}
@@ -0,0 +1,118 @@
1
+ import { cn } from './chunk-TYCPXAXF.js';
2
+ import { __objRest, __spreadValues } from './chunk-YINJ5YZ5.js';
3
+ import { jsx } from 'react/jsx-runtime';
4
+
5
+ var SIZE_BASE = {
6
+ 1: "col-span-1",
7
+ 2: "col-span-2",
8
+ 3: "col-span-3",
9
+ 4: "col-span-4",
10
+ 5: "col-span-5",
11
+ 6: "col-span-6",
12
+ 7: "col-span-7",
13
+ 8: "col-span-8",
14
+ 9: "col-span-9",
15
+ 10: "col-span-10",
16
+ 11: "col-span-11",
17
+ 12: "col-span-12"
18
+ };
19
+ var SIZE_SM = {
20
+ 1: "sm:col-span-1",
21
+ 2: "sm:col-span-2",
22
+ 3: "sm:col-span-3",
23
+ 4: "sm:col-span-4",
24
+ 5: "sm:col-span-5",
25
+ 6: "sm:col-span-6",
26
+ 7: "sm:col-span-7",
27
+ 8: "sm:col-span-8",
28
+ 9: "sm:col-span-9",
29
+ 10: "sm:col-span-10",
30
+ 11: "sm:col-span-11",
31
+ 12: "sm:col-span-12"
32
+ };
33
+ var SIZE_MD = {
34
+ 1: "md:col-span-1",
35
+ 2: "md:col-span-2",
36
+ 3: "md:col-span-3",
37
+ 4: "md:col-span-4",
38
+ 5: "md:col-span-5",
39
+ 6: "md:col-span-6",
40
+ 7: "md:col-span-7",
41
+ 8: "md:col-span-8",
42
+ 9: "md:col-span-9",
43
+ 10: "md:col-span-10",
44
+ 11: "md:col-span-11",
45
+ 12: "md:col-span-12"
46
+ };
47
+ var SIZE_LG = {
48
+ 1: "lg:col-span-1",
49
+ 2: "lg:col-span-2",
50
+ 3: "lg:col-span-3",
51
+ 4: "lg:col-span-4",
52
+ 5: "lg:col-span-5",
53
+ 6: "lg:col-span-6",
54
+ 7: "lg:col-span-7",
55
+ 8: "lg:col-span-8",
56
+ 9: "lg:col-span-9",
57
+ 10: "lg:col-span-10",
58
+ 11: "lg:col-span-11",
59
+ 12: "lg:col-span-12"
60
+ };
61
+ function Grid(_a) {
62
+ var _b = _a, {
63
+ minItemWidth,
64
+ className,
65
+ style
66
+ } = _b, props = __objRest(_b, [
67
+ "minItemWidth",
68
+ "className",
69
+ "style"
70
+ ]);
71
+ const useAutoFit = !!minItemWidth;
72
+ return /* @__PURE__ */ jsx(
73
+ "div",
74
+ __spreadValues({
75
+ "data-slot": "grid",
76
+ className: cn(
77
+ "grid gap-4 sm:gap-6",
78
+ !useAutoFit && "grid-cols-12",
79
+ className
80
+ ),
81
+ style: useAutoFit ? __spreadValues({
82
+ gridTemplateColumns: `repeat(auto-fit, minmax(${minItemWidth}, 1fr))`
83
+ }, style) : style
84
+ }, props)
85
+ );
86
+ }
87
+ function GridItem(_a) {
88
+ var _b = _a, {
89
+ size,
90
+ sizeSm,
91
+ sizeMd,
92
+ sizeLg,
93
+ className
94
+ } = _b, props = __objRest(_b, [
95
+ "size",
96
+ "sizeSm",
97
+ "sizeMd",
98
+ "sizeLg",
99
+ "className"
100
+ ]);
101
+ return /* @__PURE__ */ jsx(
102
+ "div",
103
+ __spreadValues({
104
+ "data-slot": "grid-item",
105
+ className: cn(
106
+ size && SIZE_BASE[size],
107
+ sizeSm && SIZE_SM[sizeSm],
108
+ sizeMd && SIZE_MD[sizeMd],
109
+ sizeLg && SIZE_LG[sizeLg],
110
+ className
111
+ )
112
+ }, props)
113
+ );
114
+ }
115
+
116
+ export { Grid, GridItem };
117
+ //# sourceMappingURL=chunk-JRJBKPXW.js.map
118
+ //# sourceMappingURL=chunk-JRJBKPXW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/layout/grid.tsx"],"names":[],"mappings":"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","file":"chunk-JRJBKPXW.js","sourcesContent":["import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\nimport type { ColCount } from \"./types\"\n\n/**\n * Grid — sistema de grid com 12 colunas FIXAS e gutter locked.\n *\n * Valores travados pelo design system (nao configuraveis):\n * - Columns: sempre 12\n * - Gutter (gap): 16px mobile / 24px desktop (`gap-4 sm:gap-6`)\n *\n * Items usam <GridItem size={N}> com N de 1 a 12 para ocupar fracoes da linha.\n * Para galerias auto-fit (sem cols fixas), use a prop `minItemWidth`.\n */\n\n/* ─── Size maps (1–12) — para GridItem ─────────────────────────── */\n\nconst SIZE_BASE: Record<ColCount, string> = {\n 1: \"col-span-1\", 2: \"col-span-2\", 3: \"col-span-3\", 4: \"col-span-4\",\n 5: \"col-span-5\", 6: \"col-span-6\", 7: \"col-span-7\", 8: \"col-span-8\",\n 9: \"col-span-9\", 10: \"col-span-10\", 11: \"col-span-11\", 12: \"col-span-12\",\n}\n\nconst SIZE_SM: Record<ColCount, string> = {\n 1: \"sm:col-span-1\", 2: \"sm:col-span-2\", 3: \"sm:col-span-3\", 4: \"sm:col-span-4\",\n 5: \"sm:col-span-5\", 6: \"sm:col-span-6\", 7: \"sm:col-span-7\", 8: \"sm:col-span-8\",\n 9: \"sm:col-span-9\", 10: \"sm:col-span-10\", 11: \"sm:col-span-11\", 12: \"sm:col-span-12\",\n}\n\nconst SIZE_MD: Record<ColCount, string> = {\n 1: \"md:col-span-1\", 2: \"md:col-span-2\", 3: \"md:col-span-3\", 4: \"md:col-span-4\",\n 5: \"md:col-span-5\", 6: \"md:col-span-6\", 7: \"md:col-span-7\", 8: \"md:col-span-8\",\n 9: \"md:col-span-9\", 10: \"md:col-span-10\", 11: \"md:col-span-11\", 12: \"md:col-span-12\",\n}\n\nconst SIZE_LG: Record<ColCount, string> = {\n 1: \"lg:col-span-1\", 2: \"lg:col-span-2\", 3: \"lg:col-span-3\", 4: \"lg:col-span-4\",\n 5: \"lg:col-span-5\", 6: \"lg:col-span-6\", 7: \"lg:col-span-7\", 8: \"lg:col-span-8\",\n 9: \"lg:col-span-9\", 10: \"lg:col-span-10\", 11: \"lg:col-span-11\", 12: \"lg:col-span-12\",\n}\n\n/* ─── Grid (container) ──────────────────────────────────────────── */\n\nexport interface GridProps extends React.ComponentProps<\"div\"> {\n /**\n * Auto-fit gallery: largura minima por item. Quando setado, sobrescreve\n * o sistema 12-col padrao com `repeat(auto-fit, minmax(X, 1fr))`.\n * Use para galerias uniformes onde nao se controla o numero de items por linha.\n */\n minItemWidth?: string\n}\n\nfunction Grid({\n minItemWidth,\n className,\n style,\n ...props\n}: GridProps) {\n const useAutoFit = !!minItemWidth\n\n return (\n <div\n data-slot=\"grid\"\n className={cn(\n \"grid gap-4 sm:gap-6\",\n !useAutoFit && \"grid-cols-12\",\n className\n )}\n style={\n useAutoFit\n ? {\n gridTemplateColumns: `repeat(auto-fit, minmax(${minItemWidth}, 1fr))`,\n ...style,\n }\n : style\n }\n {...props}\n />\n )\n}\n\n/* ─── GridItem (filho) ──────────────────────────────────────────── */\n\nexport interface GridItemProps extends React.ComponentProps<\"div\"> {\n /** Quantas colunas (de 12) o item ocupa no mobile. Sem size, ocupa 1 col. */\n size?: ColCount\n /** Cols em sm (>= 640px) */\n sizeSm?: ColCount\n /** Cols em md (>= 768px) */\n sizeMd?: ColCount\n /** Cols em lg (>= 1024px) */\n sizeLg?: ColCount\n}\n\nfunction GridItem({\n size,\n sizeSm,\n sizeMd,\n sizeLg,\n className,\n ...props\n}: GridItemProps) {\n return (\n <div\n data-slot=\"grid-item\"\n className={cn(\n size && SIZE_BASE[size],\n sizeSm && SIZE_SM[sizeSm],\n sizeMd && SIZE_MD[sizeMd],\n sizeLg && SIZE_LG[sizeLg],\n className\n )}\n {...props}\n />\n )\n}\n\nexport { Grid, GridItem }\n"]}
@@ -1,5 +1,5 @@
1
- import { Avatar, AvatarImage, AvatarFallback } from './chunk-MSLQRGSP.js';
2
1
  import { CycleIcon } from './chunk-V7M2NHUO.js';
2
+ import { Avatar, AvatarImage, AvatarFallback } from './chunk-MSLQRGSP.js';
3
3
  import { Badge } from './chunk-W4GSXVJ2.js';
4
4
  import { cn } from './chunk-TYCPXAXF.js';
5
5
  import * as React from 'react';
@@ -93,5 +93,5 @@ function LiveWaiting({
93
93
  }
94
94
 
95
95
  export { LiveWaiting };
96
- //# sourceMappingURL=chunk-PCLVQPIG.js.map
97
- //# sourceMappingURL=chunk-PCLVQPIG.js.map
96
+ //# sourceMappingURL=chunk-L2DJS6PG.js.map
97
+ //# sourceMappingURL=chunk-L2DJS6PG.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/ui/live-waiting.tsx"],"names":[],"mappings":"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","file":"chunk-PCLVQPIG.js","sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { RadioIcon } from \"lucide-react\"\nimport { CycleIcon } from \"@/components/icons/CycleIcon\"\nimport { Avatar, AvatarImage, AvatarFallback } from \"@/components/ui/avatar\"\nimport { Badge } from \"@/components/ui/badge\"\nimport { cn } from \"@/lib/utils\"\n\n/* ─── Types ─── */\n\nexport interface LiveWaitingProps {\n /** Scheduled start time of the live */\n scheduledAt: Date\n /** Teacher/instructor display name */\n teacherName?: string\n /** Teacher avatar URL */\n teacherAvatar?: string\n /** Live class topic/subject */\n topic?: string\n className?: string\n}\n\n/* ─── Helpers ─── */\n\ninterface TimeLeft {\n days: number\n hours: number\n minutes: number\n seconds: number\n total: number\n}\n\nfunction calcTimeLeft(target: Date): TimeLeft {\n const total = Math.max(0, target.getTime() - Date.now())\n return {\n days: Math.floor(total / (1000 * 60 * 60 * 24)),\n hours: Math.floor((total / (1000 * 60 * 60)) % 24),\n minutes: Math.floor((total / (1000 * 60)) % 60),\n seconds: Math.floor((total / 1000) % 60),\n total,\n }\n}\n\nfunction padTwo(n: number): string {\n return n.toString().padStart(2, \"0\")\n}\n\nfunction getInitials(name: string): string {\n return name\n .split(\" \")\n .slice(0, 2)\n .map((w) => w[0])\n .join(\"\")\n .toUpperCase()\n}\n\n/* ─── Sub-components ─── */\n\nfunction CountdownUnit({ value, label }: { value: string; label: string }) {\n return (\n <div className=\"flex flex-col items-center gap-1\">\n <div className=\"flex items-center justify-center size-14 sm:size-16 rounded-xl bg-muted border border-border text-2xl sm:text-3xl font-bold text-foreground tabular-nums\">\n {value}\n </div>\n <span className=\"text-[11px] text-muted-foreground uppercase tracking-wider font-medium\">\n {label}\n </span>\n </div>\n )\n}\n\n/* ─── Component ─── */\n\nfunction LiveWaiting({\n scheduledAt,\n teacherName,\n teacherAvatar,\n topic,\n className,\n}: LiveWaitingProps) {\n const [timeLeft, setTimeLeft] = React.useState<TimeLeft>(() =>\n calcTimeLeft(scheduledAt)\n )\n\n React.useEffect(() => {\n const timer = setInterval(() => {\n setTimeLeft(calcTimeLeft(scheduledAt))\n }, 1000)\n return () => clearInterval(timer)\n }, [scheduledAt])\n\n const hasStarted = timeLeft.total === 0\n\n return (\n <div\n data-slot=\"live-waiting\"\n className={cn(\n \"flex flex-col items-center justify-center gap-6 py-12 sm:py-16 px-4\",\n className\n )}\n >\n {/* Live badge */}\n <Badge variant=\"destructive\" size=\"lg\" className=\"gap-1.5\">\n <CycleIcon icon={RadioIcon} size=\"2xs\" decorative />\n {hasStarted ? \"Começando...\" : \"Aula ao Vivo\"}\n </Badge>\n\n {/* Topic */}\n {topic && (\n <h2 className=\"text-lg sm:text-xl font-semibold text-foreground text-center max-w-md leading-snug\">\n {topic}\n </h2>\n )}\n\n {/* Countdown */}\n {!hasStarted && (\n <div className=\"flex items-center gap-3\">\n {timeLeft.days > 0 && (\n <>\n <CountdownUnit value={padTwo(timeLeft.days)} label=\"Dias\" />\n <span className=\"text-2xl font-bold text-muted-foreground mt-[-20px]\">:</span>\n </>\n )}\n <CountdownUnit value={padTwo(timeLeft.hours)} label=\"Horas\" />\n <span className=\"text-2xl font-bold text-muted-foreground mt-[-20px]\">:</span>\n <CountdownUnit value={padTwo(timeLeft.minutes)} label=\"Min\" />\n <span className=\"text-2xl font-bold text-muted-foreground mt-[-20px]\">:</span>\n <CountdownUnit value={padTwo(timeLeft.seconds)} label=\"Seg\" />\n </div>\n )}\n\n {/* Scheduled date */}\n <p className=\"text-sm text-muted-foreground\">\n {scheduledAt.toLocaleDateString(\"pt-BR\", {\n weekday: \"long\",\n day: \"numeric\",\n month: \"long\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n })}\n </p>\n\n {/* Teacher info */}\n {teacherName && (\n <div className=\"flex items-center gap-3 mt-2\">\n <Avatar size=\"lg\">\n {teacherAvatar && (\n <AvatarImage src={teacherAvatar} alt={teacherName} />\n )}\n <AvatarFallback className=\"bg-primary text-primary-foreground\">\n {getInitials(teacherName)}\n </AvatarFallback>\n </Avatar>\n <div className=\"flex flex-col\">\n <span className=\"text-sm font-medium text-foreground\">\n {teacherName}\n </span>\n <span className=\"text-xs text-muted-foreground\">Professor(a)</span>\n </div>\n </div>\n )}\n </div>\n )\n}\n\nexport { LiveWaiting }\n"]}
1
+ {"version":3,"sources":["../src/components/ui/live-waiting.tsx"],"names":[],"mappings":"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","file":"chunk-L2DJS6PG.js","sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { RadioIcon } from \"lucide-react\"\nimport { CycleIcon } from \"@/components/icons/CycleIcon\"\nimport { Avatar, AvatarImage, AvatarFallback } from \"@/components/ui/avatar\"\nimport { Badge } from \"@/components/ui/badge\"\nimport { cn } from \"@/lib/utils\"\n\n/* ─── Types ─── */\n\nexport interface LiveWaitingProps {\n /** Scheduled start time of the live */\n scheduledAt: Date\n /** Teacher/instructor display name */\n teacherName?: string\n /** Teacher avatar URL */\n teacherAvatar?: string\n /** Live class topic/subject */\n topic?: string\n className?: string\n}\n\n/* ─── Helpers ─── */\n\ninterface TimeLeft {\n days: number\n hours: number\n minutes: number\n seconds: number\n total: number\n}\n\nfunction calcTimeLeft(target: Date): TimeLeft {\n const total = Math.max(0, target.getTime() - Date.now())\n return {\n days: Math.floor(total / (1000 * 60 * 60 * 24)),\n hours: Math.floor((total / (1000 * 60 * 60)) % 24),\n minutes: Math.floor((total / (1000 * 60)) % 60),\n seconds: Math.floor((total / 1000) % 60),\n total,\n }\n}\n\nfunction padTwo(n: number): string {\n return n.toString().padStart(2, \"0\")\n}\n\nfunction getInitials(name: string): string {\n return name\n .split(\" \")\n .slice(0, 2)\n .map((w) => w[0])\n .join(\"\")\n .toUpperCase()\n}\n\n/* ─── Sub-components ─── */\n\nfunction CountdownUnit({ value, label }: { value: string; label: string }) {\n return (\n <div className=\"flex flex-col items-center gap-1\">\n <div className=\"flex items-center justify-center size-14 sm:size-16 rounded-xl bg-muted border border-border text-2xl sm:text-3xl font-bold text-foreground tabular-nums\">\n {value}\n </div>\n <span className=\"text-[11px] text-muted-foreground uppercase tracking-wider font-medium\">\n {label}\n </span>\n </div>\n )\n}\n\n/* ─── Component ─── */\n\nfunction LiveWaiting({\n scheduledAt,\n teacherName,\n teacherAvatar,\n topic,\n className,\n}: LiveWaitingProps) {\n const [timeLeft, setTimeLeft] = React.useState<TimeLeft>(() =>\n calcTimeLeft(scheduledAt)\n )\n\n React.useEffect(() => {\n const timer = setInterval(() => {\n setTimeLeft(calcTimeLeft(scheduledAt))\n }, 1000)\n return () => clearInterval(timer)\n }, [scheduledAt])\n\n const hasStarted = timeLeft.total === 0\n\n return (\n <div\n data-slot=\"live-waiting\"\n className={cn(\n \"flex flex-col items-center justify-center gap-6 py-12 sm:py-16 px-4\",\n className\n )}\n >\n {/* Live badge */}\n <Badge variant=\"destructive\" size=\"lg\" className=\"gap-1.5\">\n <CycleIcon icon={RadioIcon} size=\"2xs\" decorative />\n {hasStarted ? \"Começando...\" : \"Aula ao Vivo\"}\n </Badge>\n\n {/* Topic */}\n {topic && (\n <h2 className=\"text-lg sm:text-xl font-semibold text-foreground text-center max-w-md leading-snug\">\n {topic}\n </h2>\n )}\n\n {/* Countdown */}\n {!hasStarted && (\n <div className=\"flex items-center gap-3\">\n {timeLeft.days > 0 && (\n <>\n <CountdownUnit value={padTwo(timeLeft.days)} label=\"Dias\" />\n <span className=\"text-2xl font-bold text-muted-foreground mt-[-20px]\">:</span>\n </>\n )}\n <CountdownUnit value={padTwo(timeLeft.hours)} label=\"Horas\" />\n <span className=\"text-2xl font-bold text-muted-foreground mt-[-20px]\">:</span>\n <CountdownUnit value={padTwo(timeLeft.minutes)} label=\"Min\" />\n <span className=\"text-2xl font-bold text-muted-foreground mt-[-20px]\">:</span>\n <CountdownUnit value={padTwo(timeLeft.seconds)} label=\"Seg\" />\n </div>\n )}\n\n {/* Scheduled date */}\n <p className=\"text-sm text-muted-foreground\">\n {scheduledAt.toLocaleDateString(\"pt-BR\", {\n weekday: \"long\",\n day: \"numeric\",\n month: \"long\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n })}\n </p>\n\n {/* Teacher info */}\n {teacherName && (\n <div className=\"flex items-center gap-3 mt-2\">\n <Avatar size=\"lg\">\n {teacherAvatar && (\n <AvatarImage src={teacherAvatar} alt={teacherName} />\n )}\n <AvatarFallback className=\"bg-primary text-primary-foreground\">\n {getInitials(teacherName)}\n </AvatarFallback>\n </Avatar>\n <div className=\"flex flex-col\">\n <span className=\"text-sm font-medium text-foreground\">\n {teacherName}\n </span>\n <span className=\"text-xs text-muted-foreground\">Professor(a)</span>\n </div>\n </div>\n )}\n </div>\n )\n}\n\nexport { LiveWaiting }\n"]}