@sesionario/blocks 1.0.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.
@@ -0,0 +1,700 @@
1
+ import * as React from 'react';
2
+ import { ReactNode } from 'react';
3
+ import { LucideIcon } from 'lucide-react';
4
+ import * as class_variance_authority_types from 'class-variance-authority/types';
5
+ import { VariantProps } from 'class-variance-authority';
6
+ import * as PopoverPrimitive from '@radix-ui/react-popover';
7
+ import { HlsConfig, RetryConfig, LoaderResponse } from 'hls.js';
8
+ import * as RPNInput from 'react-phone-number-input';
9
+ import { ClassValue } from 'clsx';
10
+
11
+ /**
12
+ * Blocks Context Interfaces
13
+ *
14
+ * These interfaces define the contract that consuming apps must implement
15
+ * to provide context to blocks components. Components will automatically
16
+ * consume these contexts when available, falling back to props otherwise.
17
+ */
18
+ /**
19
+ * Language definition for the language switcher.
20
+ */
21
+ interface BlocksLanguage {
22
+ code: string;
23
+ name: string;
24
+ flag?: string;
25
+ }
26
+ /**
27
+ * Language context value interface.
28
+ * Consuming apps implement this interface to provide language state
29
+ * and actions to blocks components.
30
+ */
31
+ interface LanguageContextValue {
32
+ /** Current language code (e.g., "en", "es") */
33
+ language: string;
34
+ /** Function to change the language */
35
+ setLanguage: (lang: string) => void;
36
+ /** Available languages (optional, components have defaults) */
37
+ languages?: BlocksLanguage[];
38
+ }
39
+ /**
40
+ * React context for language.
41
+ * Blocks components will consume this context when available.
42
+ */
43
+ declare const LanguageContext: React.Context<LanguageContextValue | null>;
44
+ /**
45
+ * Hook to consume the language context.
46
+ * Returns null if no provider is present (components should fall back to props).
47
+ */
48
+ declare function useLanguageContext(): LanguageContextValue | null;
49
+
50
+ declare function LoadingState({ label, className, }: {
51
+ label?: string;
52
+ className?: string;
53
+ }): React.JSX.Element;
54
+ declare function ErrorState({ title, description, action, className, }: {
55
+ title?: string;
56
+ description?: string;
57
+ action?: React.ReactNode;
58
+ className?: string;
59
+ }): React.JSX.Element;
60
+ type EmptyStateProps = {
61
+ title: string;
62
+ description?: string;
63
+ action?: React.ReactNode;
64
+ icon?: LucideIcon;
65
+ variant?: "default" | "compact";
66
+ className?: string;
67
+ };
68
+ declare function EmptyState({ title, description, action, icon: Icon, variant, className, }: EmptyStateProps): React.JSX.Element;
69
+
70
+ declare function Empty({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
71
+ declare function EmptyHeader({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
72
+ declare const emptyMediaVariants: (props?: ({
73
+ variant?: "default" | "icon" | null | undefined;
74
+ } & class_variance_authority_types.ClassProp) | undefined) => string;
75
+ declare function EmptyMedia({ className, variant, ...props }: React.ComponentProps<"div"> & VariantProps<typeof emptyMediaVariants>): React.JSX.Element;
76
+ declare function EmptyTitle({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
77
+ declare function EmptyDescription({ className, ...props }: React.ComponentProps<"p">): React.JSX.Element;
78
+ declare function EmptyContent({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
79
+
80
+ type ThemeSwitcherLabels = {
81
+ /** Tooltip label for the theme switcher */
82
+ theme?: string;
83
+ /** Label for "System" option */
84
+ system?: string;
85
+ /** Label for "Light" option */
86
+ light?: string;
87
+ /** Label for "Dark" option */
88
+ dark?: string;
89
+ };
90
+ type ThemeSwitcherProps = {
91
+ className?: string;
92
+ /** Size of the switcher */
93
+ size?: "sm" | "md" | "lg";
94
+ /** Shape of the button */
95
+ shape?: "rounded" | "pill";
96
+ /** Mini variant shows only current theme icon as dropdown */
97
+ variant?: "default" | "mini";
98
+ /** Dropdown alignment (for mini variant) */
99
+ align?: "start" | "center" | "end";
100
+ /** Dropdown side (for mini variant) */
101
+ side?: "top" | "bottom" | "left" | "right";
102
+ /** @deprecated No longer used; mini variant no longer renders a tooltip. */
103
+ tooltipSide?: "top" | "right" | "bottom" | "left";
104
+ /** @deprecated No longer used; mini variant no longer renders a tooltip. */
105
+ tooltipAlign?: "start" | "center" | "end";
106
+ /** @deprecated No longer used; mini variant no longer renders a tooltip. */
107
+ tooltipSideOffset?: number;
108
+ /** Translation labels - consumers can pass their own t() values */
109
+ labels?: ThemeSwitcherLabels;
110
+ };
111
+ declare function ThemeSwitcher({ className, size, shape, variant, align, side, labels: userLabels, }: ThemeSwitcherProps): React.JSX.Element;
112
+
113
+ type OrgSwitcherTenant = {
114
+ id: string;
115
+ name: string;
116
+ logo?: string | null;
117
+ role?: string;
118
+ statusBadge?: ReactNode;
119
+ };
120
+ type OrgSwitcherLabels = {
121
+ /** Dropdown section heading */
122
+ heading?: string;
123
+ /** Create action label */
124
+ create?: string;
125
+ /** Empty state when no active tenant */
126
+ empty?: string;
127
+ };
128
+ type OrgSwitcherProps = {
129
+ activeTenant: OrgSwitcherTenant | null;
130
+ tenants: OrgSwitcherTenant[];
131
+ onSelectTenant: (id: string) => void;
132
+ createUrl?: string;
133
+ isLoading?: boolean;
134
+ collapsed?: boolean;
135
+ isMobile?: boolean;
136
+ activeSubtitle?: string;
137
+ labels?: OrgSwitcherLabels;
138
+ className?: string;
139
+ triggerClassName?: string;
140
+ };
141
+ declare function OrgSwitcher({ activeTenant, tenants, onSelectTenant, createUrl, isLoading, collapsed, isMobile, activeSubtitle, labels: userLabels, className, triggerClassName: triggerClassNameProp, }: OrgSwitcherProps): React.JSX.Element;
142
+
143
+ type Language = {
144
+ /** Language key/code (e.g., "en", "es") */
145
+ key: string;
146
+ /** Short label for segmented control (e.g., "EN", "ES") */
147
+ label: string;
148
+ /** Full native name for dropdown (e.g., "English", "Español") */
149
+ nativeName: string;
150
+ };
151
+ type LanguageSwitcherLabels = {
152
+ /** Tooltip label for the language switcher */
153
+ language?: string;
154
+ };
155
+ /**
156
+ * Default languages when none are provided via props or context.
157
+ */
158
+ declare const defaultLanguages: Language[];
159
+ type LanguageSwitcherProps = {
160
+ /**
161
+ * Available languages. Optional if using LanguageContext.
162
+ * When context is available, context.languages is used; props override context.
163
+ * Falls back to defaultLanguages if neither provided.
164
+ */
165
+ languages?: Language[];
166
+ /**
167
+ * Current selected language key. Optional if using LanguageContext.
168
+ * When context is available, context.language is used; props override context.
169
+ */
170
+ currentLanguage?: string;
171
+ /**
172
+ * Callback when language changes. Optional if using LanguageContext.
173
+ * When context is available, context.setLanguage is used; props override context.
174
+ */
175
+ onLanguageChange?: (languageKey: string) => void;
176
+ /** Labels for accessibility/tooltips */
177
+ labels?: LanguageSwitcherLabels;
178
+ className?: string;
179
+ /** Size of the switcher */
180
+ size?: "sm" | "md" | "lg";
181
+ /** Shape of the button */
182
+ shape?: "rounded" | "squared" | "pill";
183
+ /** Mini variant shows only an icon */
184
+ variant?: "default" | "mini";
185
+ /** Show language icon in default variant */
186
+ showIcon?: boolean;
187
+ /** Dropdown alignment */
188
+ align?: "start" | "center" | "end";
189
+ /** Dropdown side */
190
+ side?: "top" | "bottom" | "left" | "right";
191
+ /** @deprecated No longer used; mini variant no longer renders a tooltip. */
192
+ tooltipSide?: "top" | "right" | "bottom" | "left";
193
+ /** @deprecated No longer used; mini variant no longer renders a tooltip. */
194
+ tooltipAlign?: "start" | "center" | "end";
195
+ /** @deprecated No longer used; mini variant no longer renders a tooltip. */
196
+ tooltipSideOffset?: number;
197
+ };
198
+ declare function LanguageSwitcher({ languages: propLanguages, currentLanguage: propCurrentLanguage, onLanguageChange: propOnLanguageChange, labels, className, size, shape, variant, showIcon, align, side, }: LanguageSwitcherProps): React.JSX.Element;
199
+
200
+ interface AvatarEditorProps {
201
+ /** Current cropped image as data URL (controlled) */
202
+ value?: string | null;
203
+ /** Callback when the cropped image changes */
204
+ onChange?: (dataUrl: string | null) => void;
205
+ /** Size of the editor container in pixels */
206
+ size?: number;
207
+ /** Whether to show grid overlay by default */
208
+ showGrid?: boolean;
209
+ /** Output image size in pixels */
210
+ outputSize?: number;
211
+ /** Output format */
212
+ outputFormat?: "png" | "jpeg" | "webp";
213
+ /** Output quality for jpeg/webp (0-1) */
214
+ outputQuality?: number;
215
+ /** Additional class names */
216
+ className?: string;
217
+ /** Control size variant - 'default' for desktop, 'large' for mobile/touch devices */
218
+ controlSize?: "default" | "large";
219
+ }
220
+ declare function AvatarEditor({ value: _value, onChange, size, showGrid: initialShowGrid, outputSize, outputFormat, outputQuality, className, controlSize, }: AvatarEditorProps): React.JSX.Element;
221
+
222
+ interface AvatarEditorDialogProps {
223
+ /** Current avatar URL or data URL */
224
+ value?: string | null;
225
+ /** Callback when avatar changes (after accepting) */
226
+ onChange?: (dataUrl: string | null) => void;
227
+ /** Async callback for saving/uploading - return true for success, false for failure */
228
+ onSave?: (dataUrl: string) => Promise<boolean> | boolean;
229
+ /** Size of the displayed avatar in pixels */
230
+ displaySize?: number;
231
+ /** Size of the editor in the dialog */
232
+ editorSize?: number;
233
+ /** Output image size */
234
+ outputSize?: number;
235
+ /** Placeholder text when no avatar */
236
+ placeholder?: string;
237
+ /** Edit button label for accessibility */
238
+ editLabel?: string;
239
+ /** Dialog title */
240
+ dialogTitle?: string;
241
+ /** Accept button text */
242
+ acceptText?: string;
243
+ /** Cancel button text */
244
+ cancelText?: string;
245
+ /** Success message */
246
+ successMessage?: string;
247
+ /** Error message */
248
+ errorMessage?: string;
249
+ /** Additional class names */
250
+ className?: string;
251
+ }
252
+ declare function AvatarEditorDialog({ value, onChange, onSave, displaySize, editorSize, outputSize, placeholder, editLabel, dialogTitle, acceptText, cancelText, successMessage, errorMessage, className, }: AvatarEditorDialogProps): React.JSX.Element;
253
+
254
+ /** Category keys, in display order. */
255
+ type EmojiCategoryKey = "smileys" | "people" | "nature" | "food" | "travel" | "activities" | "objects" | "symbols" | "flags";
256
+ interface EmojiDatum {
257
+ /** The emoji glyph to insert. */
258
+ e: string;
259
+ /** English label, for aria-label / tooltip. */
260
+ n: string;
261
+ /** Normalized bilingual (en+es) search haystack; space-joined tokens. */
262
+ k: string;
263
+ /** Uniform skin-tone variants, tone 1..5. Present only when applicable. */
264
+ t?: string[];
265
+ }
266
+ interface EmojiCategoryData {
267
+ key: EmojiCategoryKey;
268
+ emojis: EmojiDatum[];
269
+ }
270
+
271
+ /** 0 = default (no modifier / yellow); 1..5 = Fitzpatrick light..dark. */
272
+ type EmojiSkinTone = 0 | 1 | 2 | 3 | 4 | 5;
273
+ type EmojiPickerLocale = "en" | "es";
274
+ interface EmojiPickerLabels {
275
+ searchPlaceholder?: string;
276
+ searchAriaLabel?: string;
277
+ noResults?: string;
278
+ recentTitle?: string;
279
+ skinToneAriaLabel?: string;
280
+ /** Overrides the built-in category section titles (keyed by category, plus
281
+ * `recent`). When omitted, titles come from `locale`. */
282
+ categoryTitles?: Partial<Record<EmojiCategoryKey | "recent", string>>;
283
+ }
284
+ interface EmojiPickerProps {
285
+ /** Called with the (skin-tone-applied) emoji glyph when one is chosen. */
286
+ onSelect: (emoji: string) => void;
287
+ /** Drives the default section titles + skin-tone labels. Search is always
288
+ * bilingual regardless. Default `"en"`. */
289
+ locale?: EmojiPickerLocale;
290
+ labels?: EmojiPickerLabels;
291
+ className?: string;
292
+ /** How many recently-used emoji to remember. Default 24. */
293
+ recentLimit?: number;
294
+ /** localStorage namespace for recents + skin-tone preference. Default
295
+ * `"blocks-emoji"`. Set to `null` to disable persistence entirely. */
296
+ storageKey?: string | null;
297
+ }
298
+ /**
299
+ * Normalizes a search query the same way keyword haystacks are normalized at
300
+ * generation time (see `scripts/generate-emoji-data.mjs`): lowercased, accents
301
+ * stripped, punctuation collapsed to spaces. Keeping the two in lockstep is
302
+ * what lets "corazón", "corazon" and "heart" all match the same emoji.
303
+ */
304
+ declare function normalizeEmojiQuery(input: string): string;
305
+ /**
306
+ * Zero-dependency emoji picker with bilingual (English + Spanish) search,
307
+ * category navigation, a persisted "frequently used" section, a global skin
308
+ * tone preference, and keyboard navigation (desktop-first). Usable standalone
309
+ * or inside {@link EmojiPickerPopover}.
310
+ */
311
+ declare function EmojiPicker({ onSelect, locale, labels, className, recentLimit, storageKey, }: EmojiPickerProps): React.JSX.Element;
312
+
313
+ declare function PopoverContent({ className, align, sideOffset, ...props }: React.ComponentProps<typeof PopoverPrimitive.Content>): React.JSX.Element;
314
+
315
+ interface EmojiPickerPopoverProps extends EmojiPickerProps {
316
+ /** aria-label for the trigger button. Defaults by `locale`. */
317
+ triggerLabel?: string;
318
+ /** Extra classes for the trigger button. */
319
+ triggerClassName?: string;
320
+ /** Popover side. Default `"top"`. */
321
+ side?: React.ComponentProps<typeof PopoverContent>["side"];
322
+ /** Popover alignment. Default `"end"`. */
323
+ align?: React.ComponentProps<typeof PopoverContent>["align"];
324
+ /** Controlled open state (optional). */
325
+ open?: boolean;
326
+ onOpenChange?: (open: boolean) => void;
327
+ /** Keep the popover open after choosing an emoji (e.g. to add several).
328
+ * Default `false`. */
329
+ keepOpenOnSelect?: boolean;
330
+ }
331
+ /**
332
+ * Convenience wrapper: a `Smile` icon button that opens {@link EmojiPicker} in
333
+ * a popover. Drop it next to a textarea/input; wire `onSelect` to insert at the
334
+ * caret. Uncontrolled by default; pass `open`/`onOpenChange` to control it.
335
+ */
336
+ declare function EmojiPickerPopover({ onSelect, triggerLabel, triggerClassName, side, align, open, onOpenChange, keepOpenOnSelect, locale, ...pickerProps }: EmojiPickerPopoverProps): React.JSX.Element;
337
+
338
+ /**
339
+ * Playback error description, ported from the app's HLS player but stripped of
340
+ * Sentry. `@sesionario/blocks` must stay reporting-agnostic: the player surfaces
341
+ * a {@link DescribedPlaybackError} through an optional `onError` prop so each
342
+ * app can wire its own reporting (Sentry, Datadog, console, …).
343
+ *
344
+ * User-facing messages are intentionally NOT baked in here — they come from the
345
+ * player's `labels` prop so they stay translatable. This module only classifies
346
+ * the error and produces a machine-readable summary + detail for logging.
347
+ */
348
+ type HlsPlayerMode = "live" | "vod";
349
+ /** Coarse category the player uses to pick the right localized message. */
350
+ type PlaybackErrorKind = "unavailable" | "network" | "media" | "unsupported" | "generic";
351
+ type PlaybackErrorContext = {
352
+ mode: HlsPlayerMode;
353
+ src: string;
354
+ manifestParsed: boolean;
355
+ retryCount?: number;
356
+ source: "hls" | "native" | "unsupported";
357
+ };
358
+ type PlaybackErrorDetail = {
359
+ source: PlaybackErrorContext["source"];
360
+ kind: PlaybackErrorKind;
361
+ type?: string;
362
+ details?: string;
363
+ fatal?: boolean;
364
+ httpStatus?: number;
365
+ url?: string;
366
+ reason?: string;
367
+ errorMessage?: string;
368
+ mediaErrorCode?: number;
369
+ mediaErrorName?: string;
370
+ context: PlaybackErrorContext;
371
+ };
372
+ type DescribePlaybackErrorInput = {
373
+ source: "hls";
374
+ type?: string;
375
+ details?: string;
376
+ fatal: boolean;
377
+ url?: string;
378
+ response?: {
379
+ code?: number;
380
+ text?: string;
381
+ };
382
+ reason?: string;
383
+ error?: unknown;
384
+ context: PlaybackErrorContext;
385
+ } | {
386
+ source: "native";
387
+ mediaError?: MediaError | null;
388
+ context: PlaybackErrorContext;
389
+ } | {
390
+ source: "unsupported";
391
+ context: PlaybackErrorContext;
392
+ };
393
+ type DescribedPlaybackError = {
394
+ /** Machine-readable one-line summary for logs. */
395
+ summary: string;
396
+ /** Coarse category for choosing a localized message. */
397
+ kind: PlaybackErrorKind;
398
+ detail: PlaybackErrorDetail;
399
+ };
400
+ declare function describePlaybackError(input: DescribePlaybackErrorInput): DescribedPlaybackError;
401
+
402
+ interface PlaybackStats {
403
+ /** Concurrent live viewers (meaningful in live mode). */
404
+ viewers?: number;
405
+ /** Cumulative VOD reproductions. */
406
+ views?: number;
407
+ }
408
+ interface UsePlaybackStatsOptions {
409
+ /** Poll interval in ms. Default 15000. */
410
+ intervalMs?: number;
411
+ /** When false, polling is paused. Default true. */
412
+ enabled?: boolean;
413
+ }
414
+ /**
415
+ * Polls a playback-stats endpoint (media-svc `GET /playback/:mediaId/stats`)
416
+ * and returns `{ viewers, views }`. Plain `fetch`, zero dependencies, so wiring
417
+ * it into an app is a one-liner. Fails silently (stats are non-critical); the
418
+ * last successful value is retained across transient errors.
419
+ */
420
+ declare function usePlaybackStats(statsUrl: string | null | undefined, { intervalMs, enabled }?: UsePlaybackStatsOptions): PlaybackStats;
421
+
422
+ interface VideoPlayerLabels {
423
+ play?: string;
424
+ pause?: string;
425
+ mute?: string;
426
+ unmute?: string;
427
+ enterFullscreen?: string;
428
+ exitFullscreen?: string;
429
+ pictureInPicture?: string;
430
+ settings?: string;
431
+ quality?: string;
432
+ auto?: string;
433
+ speed?: string;
434
+ live?: string;
435
+ goLive?: string;
436
+ tapToUnmute?: string;
437
+ loading?: string;
438
+ waiting?: string;
439
+ retry?: string;
440
+ /** Messages by error kind; a generic fallback covers the rest. */
441
+ errorUnavailable?: string;
442
+ errorNetwork?: string;
443
+ errorMedia?: string;
444
+ errorUnsupported?: string;
445
+ errorGeneric?: string;
446
+ /** `(n) => "N watching"`. */
447
+ formatViewers?: (n: number) => string;
448
+ /** `(n) => "N views"`. */
449
+ formatViews?: (n: number) => string;
450
+ }
451
+ interface VideoPlayerProps {
452
+ src: string;
453
+ mode: "live" | "vod";
454
+ /** Cover image; renders a click-to-play state before attaching HLS. */
455
+ poster?: string;
456
+ autoPlay?: boolean;
457
+ muted?: boolean;
458
+ /** Presentational stats (see `usePlaybackStats`). */
459
+ stats?: PlaybackStats;
460
+ labels?: VideoPlayerLabels;
461
+ /** Show the resolution picker when multiple levels exist. Default true. */
462
+ showQualitySelector?: boolean;
463
+ onPlayingChange?: (playing: boolean) => void;
464
+ /** Fires once, the first time playback actually starts — wire your view
465
+ * beacon here. */
466
+ onFirstPlay?: () => void;
467
+ onError?: (error: DescribedPlaybackError) => void;
468
+ className?: string;
469
+ }
470
+ /**
471
+ * Standardized HLS video player with a fully custom control bar (no native
472
+ * `controls`): click-to-play poster, play/pause, mute + volume, resolution
473
+ * picker, playback speed (VOD), Picture-in-Picture, fullscreen, a VOD seekbar
474
+ * with buffered ranges, and a live badge (no seekbar) with a "go live"
475
+ * affordance. `stats` renders live viewer / VOD reproduction counts.
476
+ *
477
+ * hls.js is a peer dependency loaded on demand; see {@link useHlsPlayback}.
478
+ */
479
+ declare function VideoPlayer({ src, mode, poster, autoPlay, muted, stats, labels, showQualitySelector, onPlayingChange, onFirstPlay, onError, className, }: VideoPlayerProps): React.JSX.Element;
480
+
481
+ type PlaybackState = "idle" | "loading" | "ready" | "waiting" | "error";
482
+ /** Autoplay overlay state once the manifest is parsed. */
483
+ type AutoplayState = "none" | "playing" | "muted" | "blocked";
484
+ interface QualityLevel {
485
+ /** hls.js level index. */
486
+ index: number;
487
+ /** Vertical resolution, e.g. 720. */
488
+ height: number;
489
+ }
490
+ interface UseHlsPlaybackOptions {
491
+ src: string;
492
+ mode: HlsPlayerMode;
493
+ /** When false, nothing is attached/loaded (poster gating). */
494
+ active: boolean;
495
+ /** Attempt autoplay once the manifest is ready. Live always autoplays. */
496
+ autoPlay?: boolean;
497
+ /** Start muted (independent of the autoplay cascade). */
498
+ muted?: boolean;
499
+ onError?: (error: DescribedPlaybackError) => void;
500
+ onPlayingChange?: (playing: boolean) => void;
501
+ }
502
+ interface UseHlsPlaybackResult {
503
+ videoRef: React.RefObject<HTMLVideoElement | null>;
504
+ state: PlaybackState;
505
+ /** Available quality levels (empty for native HLS / single-level). */
506
+ qualities: QualityLevel[];
507
+ /** Selected level index, or -1 for automatic. */
508
+ activeLevel: number;
509
+ setLevel: (index: number) => void;
510
+ autoplayState: AutoplayState;
511
+ /** The classified error, when `state === "error"`. */
512
+ error: DescribedPlaybackError | null;
513
+ retry: () => void;
514
+ }
515
+ /**
516
+ * Attaches hls.js (loaded on demand via dynamic import) or native HLS to a
517
+ * `<video>` element and exposes the state a custom control bar needs. Ported
518
+ * from the app's production `HlsPlayer` effect, minus the built-in UI and
519
+ * Sentry: errors are classified and handed to `onError`.
520
+ */
521
+ declare function useHlsPlayback({ src, mode, active, autoPlay, muted, onError, onPlayingChange, }: UseHlsPlaybackOptions): UseHlsPlaybackResult;
522
+
523
+ /** Retry transient live-edge 404s; hls.js skips all 4xx by default. */
524
+ declare function shouldRetryLiveEdge404(retryConfig: RetryConfig | null | undefined, retryCount: number, _isTimeout: boolean, loaderResponse: LoaderResponse | undefined, defaultRetry: boolean): boolean;
525
+ /**
526
+ * HLS.js options tuned for live playback through a Cloudflare R2 custom domain.
527
+ * Ported from the app's production player: stays 4 segments (~8s) behind the
528
+ * live edge so transient upload lag never reaches the playhead, and retries the
529
+ * 404s that appear while a new segment is still being written.
530
+ */
531
+ declare function createLiveHlsConfig(): Partial<HlsConfig>;
532
+ /**
533
+ * HLS.js options for on-demand (VOD) playback. No live-edge cushion; enables
534
+ * the worker and lets hls.js manage the buffer with its defaults, which are
535
+ * well suited to seekable recordings.
536
+ */
537
+ declare function createVodHlsConfig(): Partial<HlsConfig>;
538
+
539
+ /**
540
+ * Debounces a value — useful for ZIP lookups and autocomplete queries.
541
+ */
542
+ declare function useDebouncedValue<T>(value: T, delayMs: number): T;
543
+ /**
544
+ * Like {@link useDebouncedValue} but the returned value is `undefined` until
545
+ * the first `delayMs` has elapsed for the current `value`, and resets to
546
+ * `undefined` whenever `value` changes. Use for “only act after user pauses”
547
+ * (e.g. auto-submit / auto-validate) without firing on the initial tick.
548
+ */
549
+ declare function useDebouncedValueStrict<T>(value: T, delayMs: number): T | undefined;
550
+
551
+ interface UseSearchHotkeyOptions {
552
+ /** Initial open state. Defaults to `false`. */
553
+ defaultOpen?: boolean;
554
+ /** Disables the keyboard listener without unmounting the palette. */
555
+ disabled?: boolean;
556
+ }
557
+ interface UseSearchHotkeyResult {
558
+ open: boolean;
559
+ setOpen: (open: boolean) => void;
560
+ toggle: () => void;
561
+ }
562
+ /**
563
+ * Manages open state for a global command palette and wires up the
564
+ * Ctrl/Cmd+K shortcut to toggle it. Ctrl+F is deliberately left alone —
565
+ * overriding the browser's native find is bad UX.
566
+ */
567
+ declare function useSearchHotkey(options?: UseSearchHotkeyOptions): UseSearchHotkeyResult;
568
+
569
+ /**
570
+ * A search result returned by search-svc's `GET /search` after ACL
571
+ * hydration. Mirrors `search-svc/src/domain/search/types.ts` `SearchHit` —
572
+ * kept duplicated (not imported) since blocks has no dependency on any
573
+ * backend service.
574
+ */
575
+ interface SearchHit {
576
+ id: string;
577
+ type: string;
578
+ service: string;
579
+ entityId: string;
580
+ orgId: string | null;
581
+ userId: string | null;
582
+ visibility: string;
583
+ title: string;
584
+ snippet: string;
585
+ url: string;
586
+ payload: Record<string, unknown>;
587
+ score: number;
588
+ }
589
+ /** A static, per-app navigable feature/functionality (not user content). */
590
+ interface FeatureEntry {
591
+ id: string;
592
+ label: string;
593
+ href: string;
594
+ icon?: ReactNode;
595
+ /** Extra terms matched against the query (aliases, section name, etc). */
596
+ keywords?: string[];
597
+ /** Optional sub-group label, e.g. "Configuración" vs "Navegación". */
598
+ group?: string;
599
+ }
600
+ /** What the caller's `onSelect` receives — a feature or a hydrated search hit. */
601
+ type SearchCommandSelection = {
602
+ kind: "feature";
603
+ feature: FeatureEntry;
604
+ } | {
605
+ kind: "hit";
606
+ hit: SearchHit;
607
+ };
608
+ interface SearchCommandLabels {
609
+ /** Input placeholder. */
610
+ placeholder?: string;
611
+ /** Dialog title (visually hidden, read by screen readers). */
612
+ dialogTitle?: string;
613
+ /** Dialog description (visually hidden, read by screen readers). */
614
+ dialogDescription?: string;
615
+ /** Shown while `fetchResults` is in flight. */
616
+ loading?: string;
617
+ /** Shown when there are no features and no hits for the current query. */
618
+ empty?: string;
619
+ /** Shown when `fetchResults` rejects. */
620
+ error?: string;
621
+ /** Heading for the static features group. */
622
+ featuresGroup?: string;
623
+ /** Fallback heading for a hit type with no entry in `typeLabels`. */
624
+ defaultResultsGroup?: string;
625
+ }
626
+
627
+ interface SearchCommandProps {
628
+ open: boolean;
629
+ onOpenChange: (open: boolean) => void;
630
+ /** Transport-agnostic fetcher — each app supplies its own (proxy route, RPC, etc). */
631
+ fetchResults: (query: string) => Promise<SearchHit[]>;
632
+ onSelect: (selection: SearchCommandSelection) => void;
633
+ /** Static per-app registry of navigable features, filtered client-side. */
634
+ features?: FeatureEntry[];
635
+ /** Maps a `SearchHit.type` to a display group heading, e.g. `{ event: "Eventos" }`. */
636
+ typeLabels?: Record<string, string>;
637
+ labels?: SearchCommandLabels;
638
+ /** Query must reach this length before `fetchResults` is called. Default 2. */
639
+ minQueryLength?: number;
640
+ /** Debounce delay before calling `fetchResults`. Default 200ms. */
641
+ debounceMs?: number;
642
+ className?: string;
643
+ }
644
+ declare function SearchCommand({ open, onOpenChange, fetchResults, onSelect, features, typeLabels, labels: userLabels, minQueryLength, debounceMs, className, }: SearchCommandProps): React.JSX.Element;
645
+
646
+ /**
647
+ * Minimal, accent-insensitive fuzzy match for the static feature registry.
648
+ * Not used for server-returned search hits — those come back already
649
+ * relevance-ranked (vector + keyword RRF) and must never be hidden by a
650
+ * literal-substring filter, since semantic matches often don't share
651
+ * substrings with the query.
652
+ */
653
+ /**
654
+ * True if `query` is a substring of `target`, or a subsequence of it (i.e.
655
+ * every query character appears in `target`, in order, possibly with gaps).
656
+ * Both comparisons are case- and accent-insensitive.
657
+ */
658
+ declare function fuzzyMatch(query: string, target: string): boolean;
659
+
660
+ /**
661
+ * International phone input with country picker (default: Mexico).
662
+ * Consuming apps must import `react-phone-number-input/style.css` in their root layout.
663
+ */
664
+
665
+ type PhoneInputProps = Omit<React.ComponentProps<"input">, "onChange" | "value" | "ref"> & Omit<RPNInput.Props<typeof RPNInput.default>, "onChange" | "defaultCountry"> & {
666
+ defaultCountry?: RPNInput.Country;
667
+ onChange?: (value: RPNInput.Value) => void;
668
+ };
669
+ declare const PhoneInput: React.ForwardRefExoticComponent<PhoneInputProps>;
670
+
671
+ type MexicoStateId = "MXAGU" | "MXBCN" | "MXBCS" | "MXCAM" | "MXCHP" | "MXCHH" | "MXCMX" | "MXCOA" | "MXCOL" | "MXDUR" | "MXGUA" | "MXGRO" | "MXHID" | "MXJAL" | "MXMEX" | "MXMIC" | "MXMOR" | "MXNAY" | "MXNLE" | "MXOAX" | "MXPUE" | "MXQUE" | "MXROO" | "MXSLP" | "MXSIN" | "MXSON" | "MXTAB" | "MXTAM" | "MXTLA" | "MXVER" | "MXYUC" | "MXZAC";
672
+ interface MexicoState {
673
+ id: MexicoStateId;
674
+ name: string;
675
+ }
676
+ declare const MEXICO_STATES: MexicoState[];
677
+
678
+ interface MexicoMapProps {
679
+ selected: MexicoStateId[];
680
+ /** Omit to render a read-only display (no interaction, no keyboard handlers). */
681
+ onToggle?: (id: MexicoStateId) => void;
682
+ className?: string;
683
+ }
684
+ declare function MexicoMap({ selected, onToggle, className }: MexicoMapProps): React.JSX.Element;
685
+
686
+ type ProfessionalVerificationStatus = "unverified" | "pending_review" | "verified";
687
+ interface VerifiedProfessionalBadgeProps {
688
+ status: ProfessionalVerificationStatus;
689
+ size?: "sm" | "md";
690
+ /** Icon-only with title/aria-label when false (default). */
691
+ showLabel?: boolean;
692
+ className?: string;
693
+ }
694
+ declare function VerifiedProfessionalBadge({ status, size, showLabel, className, }: VerifiedProfessionalBadgeProps): React.JSX.Element | null;
695
+
696
+ declare const MEXICO_STATE_PATHS: Record<MexicoStateId, string>;
697
+
698
+ declare function cn(...inputs: ClassValue[]): string;
699
+
700
+ export { type AutoplayState, AvatarEditor, AvatarEditorDialog, type AvatarEditorDialogProps, type AvatarEditorProps, type BlocksLanguage, type DescribedPlaybackError, type EmojiCategoryData, type EmojiCategoryKey, type EmojiDatum, EmojiPicker, type EmojiPickerLabels, type EmojiPickerLocale, EmojiPickerPopover, type EmojiPickerPopoverProps, type EmojiPickerProps, type EmojiSkinTone, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyState, type EmptyStateProps, EmptyTitle, ErrorState, type FeatureEntry, type HlsPlayerMode, type Language, LanguageContext, type LanguageContextValue, LanguageSwitcher, type LanguageSwitcherLabels, type LanguageSwitcherProps, LoadingState, MEXICO_STATES, MEXICO_STATE_PATHS, MexicoMap, type MexicoMapProps, type MexicoState, type MexicoStateId, OrgSwitcher, type OrgSwitcherLabels, type OrgSwitcherProps, type OrgSwitcherTenant, PhoneInput, type PhoneInputProps, type PlaybackErrorContext, type PlaybackErrorDetail, type PlaybackErrorKind, type PlaybackState, type PlaybackStats, type ProfessionalVerificationStatus, type QualityLevel, SearchCommand, type SearchCommandLabels, type SearchCommandProps, type SearchCommandSelection, type SearchHit, ThemeSwitcher, type ThemeSwitcherLabels, type ThemeSwitcherProps, type UseHlsPlaybackOptions, type UseHlsPlaybackResult, type UsePlaybackStatsOptions, type UseSearchHotkeyOptions, type UseSearchHotkeyResult, VerifiedProfessionalBadge, type VerifiedProfessionalBadgeProps, VideoPlayer, type VideoPlayerLabels, type VideoPlayerProps, cn, createLiveHlsConfig, createVodHlsConfig, defaultLanguages, describePlaybackError, fuzzyMatch, normalizeEmojiQuery, shouldRetryLiveEdge404, useDebouncedValue, useDebouncedValueStrict, useHlsPlayback, useLanguageContext, usePlaybackStats, useSearchHotkey };