@optifye/dashboard-core 6.9.8 → 6.9.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -7,17 +7,15 @@ import { subDays, format, parseISO, isValid, formatDistanceToNow, isFuture, isTo
7
7
  import mixpanel from 'mixpanel-browser';
8
8
  import { EventEmitter } from 'events';
9
9
  import { createClient, REALTIME_SUBSCRIBE_STATES } from '@supabase/supabase-js';
10
- import Hls2 from 'hls.js';
10
+ import Hls3, { Events, ErrorTypes } from 'hls.js';
11
11
  import useSWR from 'swr';
12
12
  import { noop, warning, invariant, progress, secondsToMilliseconds, millisecondsToSeconds, memo as memo$1 } from 'motion-utils';
13
13
  import { getValueTransition, hover, press, isPrimaryPointer, GroupPlaybackControls, setDragLock, supportsLinearEasing, attachTimeline, isGenerator, calcGeneratorDuration, isWaapiSupportedEasing, mapEasingToNativeEasing, maxGeneratorDuration, generateLinearEasing, isBezierDefinition } from 'motion-dom';
14
- import { Camera, ChevronDown, ChevronUp, Check, Map as Map$1, Video, ShieldCheck, Star, Award, ArrowLeft, X, Coffee, Plus, Clock, Calendar, Save, AlertCircle, Loader2, Minus, ArrowDown, ArrowUp, ChevronLeft, ChevronRight, Pause, Play, XCircle, Maximize2, Sparkles, TrendingUp, Settings2, CheckCircle2, RefreshCw, TrendingDown, FolderOpen, Folder, HelpCircle, Sliders, Activity, Layers, Filter, Search, Edit2, AlertTriangle, CheckCircle, Building2, Mail, Users, User, Lock, ArrowRight, Info, Share2, Trophy, Target, Download, Sun, Moon, MousePointer, MessageSquare, Trash2, Menu, Send, Copy, UserCheck, LogOut, Package, UserPlus, Settings, LifeBuoy, EyeOff, Eye, MoreVertical, UserCog, Zap, Shield, UserCircle } from 'lucide-react';
14
+ import { Camera, ChevronDown, ChevronUp, Check, Map as Map$1, Video, ShieldCheck, Star, Award, ArrowLeft, X, Coffee, Plus, Clock, Calendar, Save, AlertCircle, Loader2, Minus, ArrowDown, ArrowUp, ChevronLeft, ChevronRight, Pause, Play, XCircle, Sparkles, TrendingUp, Settings2, CheckCircle2, RefreshCw, TrendingDown, FolderOpen, Folder, HelpCircle, Sliders, Activity, Layers, Filter, Search, Edit2, AlertTriangle, CheckCircle, Building2, Mail, Users, User, Lock, ArrowRight, Info, Share2, Trophy, Target, Download, Sun, Moon, MousePointer, MessageSquare, Trash2, Menu, Send, Copy, UserCheck, LogOut, Package, UserPlus, Settings, LifeBuoy, EyeOff, Eye, MoreVertical, UserCog, Zap, Shield, UserCircle } from 'lucide-react';
15
15
  import { toast } from 'sonner';
16
16
  import { BarChart as BarChart$1, CartesianGrid, XAxis, YAxis, Tooltip, Legend, Bar, LabelList, ResponsiveContainer, LineChart as LineChart$1, Line, PieChart, Pie, Cell, ReferenceLine, ComposedChart, Area, ScatterChart, Scatter } from 'recharts';
17
17
  import { Slot } from '@radix-ui/react-slot';
18
18
  import * as SelectPrimitive from '@radix-ui/react-select';
19
- import videojs from 'video.js';
20
- import 'video.js/dist/video-js.css';
21
19
  import { DayPicker, useNavigation as useNavigation$1 } from 'react-day-picker';
22
20
  import { XMarkIcon, ArrowRightIcon, HomeIcon, TrophyIcon, ChartBarIcon, AdjustmentsHorizontalIcon, ClockIcon, UsersIcon, TicketIcon, CubeIcon, SparklesIcon, QuestionMarkCircleIcon, HeartIcon, UserCircleIcon, ExclamationCircleIcon, EnvelopeIcon, DocumentTextIcon, ChevronUpIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, Bars3Icon, CheckCircleIcon, ChatBubbleLeftRightIcon, ArrowLeftIcon, XCircleIcon, InformationCircleIcon } from '@heroicons/react/24/outline';
23
21
  import { CheckIcon } from '@heroicons/react/24/solid';
@@ -8989,12 +8987,12 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError }) {
8989
8987
  const video = videoRef.current;
8990
8988
  if (!video) return;
8991
8989
  isNativeHlsRef.current = video.canPlayType("application/vnd.apple.mpegurl") === "probably";
8992
- if (Hls2.isSupported() && !isNativeHlsRef.current) {
8993
- const hls = new Hls2(HLS_CONFIG);
8990
+ if (Hls3.isSupported() && !isNativeHlsRef.current) {
8991
+ const hls = new Hls3(HLS_CONFIG);
8994
8992
  hlsRef.current = hls;
8995
8993
  hls.attachMedia(video);
8996
8994
  hls.loadSource(src);
8997
- hls.on(Hls2.Events.ERROR, (_, data) => {
8995
+ hls.on(Hls3.Events.ERROR, (_, data) => {
8998
8996
  if (!data.fatal) return;
8999
8997
  console.error("[HLS] Fatal error:", data.type, data.details);
9000
8998
  if (data.response?.code === 404) {
@@ -9002,8 +9000,8 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError }) {
9002
9000
  return;
9003
9001
  }
9004
9002
  switch (data.type) {
9005
- case Hls2.ErrorTypes.NETWORK_ERROR:
9006
- case Hls2.ErrorTypes.MEDIA_ERROR:
9003
+ case Hls3.ErrorTypes.NETWORK_ERROR:
9004
+ case Hls3.ErrorTypes.MEDIA_ERROR:
9007
9005
  softRestart(`${data.type}: ${data.details}`);
9008
9006
  break;
9009
9007
  default:
@@ -9011,7 +9009,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError }) {
9011
9009
  break;
9012
9010
  }
9013
9011
  });
9014
- hls.on(Hls2.Events.MANIFEST_PARSED, () => {
9012
+ hls.on(Hls3.Events.MANIFEST_PARSED, () => {
9015
9013
  if (failedUrls.has(src)) {
9016
9014
  console.log(`[HLS] Stream loaded successfully, resetting failure count for: ${src}`);
9017
9015
  failedUrls.delete(src);
@@ -14054,9 +14052,9 @@ var S3VideoPreloader = class {
14054
14052
  this.processQueue();
14055
14053
  };
14056
14054
  if (url.endsWith(".m3u8")) {
14057
- import('hls.js').then(({ default: Hls3 }) => {
14058
- if (Hls3.isSupported()) {
14059
- const hls = new Hls3({
14055
+ import('hls.js').then(({ default: Hls4 }) => {
14056
+ if (Hls4.isSupported()) {
14057
+ const hls = new Hls4({
14060
14058
  maxBufferLength: 10,
14061
14059
  startFragPrefetch: true,
14062
14060
  lowLatencyMode: false,
@@ -14068,7 +14066,7 @@ var S3VideoPreloader = class {
14068
14066
  hls.destroy();
14069
14067
  cleanup();
14070
14068
  }, 4e3);
14071
- hls.on(Hls3.Events.BUFFER_APPENDED, () => {
14069
+ hls.on(Hls4.Events.BUFFER_APPENDED, () => {
14072
14070
  window.clearTimeout(timeout);
14073
14071
  hls.destroy();
14074
14072
  cleanup();
@@ -23072,7 +23070,7 @@ var OutputProgressChartComponent = ({
23072
23070
  ];
23073
23071
  const COLORS = ["#00AB45", "#f3f4f6"];
23074
23072
  const percentage = (currentOutput / targetOutput * 100).toFixed(1);
23075
- return /* @__PURE__ */ jsx("div", { className: `w-full h-full flex items-center justify-center ${className}`, children: /* @__PURE__ */ jsxs("div", { className: "relative w-full max-w-[200px] sm:max-w-[240px] lg:max-w-[280px] aspect-square", style: { minHeight: "120px", minWidth: "120px" }, children: [
23073
+ return /* @__PURE__ */ jsx("div", { className: `w-full h-full flex items-center justify-center ${className}`, children: /* @__PURE__ */ jsxs("div", { className: "relative w-full aspect-square max-h-full", style: { maxWidth: "min(100%, 280px)" }, children: [
23076
23074
  /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsx(PieChart, { children: /* @__PURE__ */ jsx(
23077
23075
  Pie,
23078
23076
  {
@@ -26024,12 +26022,6 @@ var AxelNotificationPopup = ({
26024
26022
  };
26025
26023
 
26026
26024
  // src/views/components/workspace/BottlenecksContent.utils.ts
26027
- var formatTime2 = (seconds) => {
26028
- if (isNaN(seconds)) return "0:00";
26029
- const minutes = Math.floor(seconds / 60);
26030
- const remainingSeconds = Math.floor(seconds % 60);
26031
- return `${minutes}:${remainingSeconds < 10 ? "0" : ""}${remainingSeconds}`;
26032
- };
26033
26025
  var getSeverityColor = (severity) => {
26034
26026
  switch (severity) {
26035
26027
  case "low":
@@ -26042,473 +26034,6 @@ var getSeverityColor = (severity) => {
26042
26034
  return "bg-gray-500";
26043
26035
  }
26044
26036
  };
26045
- function Skeleton({ className, ...props }) {
26046
- return /* @__PURE__ */ jsx("div", { className: cn("animate-pulse rounded-md bg-muted", className), ...props });
26047
- }
26048
- var Select = SelectPrimitive.Root;
26049
- var SelectGroup = SelectPrimitive.Group;
26050
- var SelectValue = SelectPrimitive.Value;
26051
- var SelectTrigger = React23.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
26052
- SelectPrimitive.Trigger,
26053
- {
26054
- ref,
26055
- className: cn(
26056
- "flex h-11 sm:h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background data-[placeholder]:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1 touch-manipulation",
26057
- className
26058
- ),
26059
- ...props,
26060
- children: [
26061
- children,
26062
- /* @__PURE__ */ jsx(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4 opacity-50" }) })
26063
- ]
26064
- }
26065
- ));
26066
- SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
26067
- var SelectScrollUpButton = React23.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
26068
- SelectPrimitive.ScrollUpButton,
26069
- {
26070
- ref,
26071
- className: cn("flex cursor-default items-center justify-center py-1", className),
26072
- ...props,
26073
- children: /* @__PURE__ */ jsx(ChevronUp, { className: "h-4 w-4" })
26074
- }
26075
- ));
26076
- SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
26077
- var SelectScrollDownButton = React23.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
26078
- SelectPrimitive.ScrollDownButton,
26079
- {
26080
- ref,
26081
- className: cn("flex cursor-default items-center justify-center py-1", className),
26082
- ...props,
26083
- children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4" })
26084
- }
26085
- ));
26086
- SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
26087
- var SelectContent = React23.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
26088
- SelectPrimitive.Content,
26089
- {
26090
- ref,
26091
- className: cn(
26092
- "relative z-50 max-h-[--radix-select-content-available-height] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-select-content-transform-origin]",
26093
- position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
26094
- className
26095
- ),
26096
- position,
26097
- ...props,
26098
- children: [
26099
- /* @__PURE__ */ jsx(SelectScrollUpButton, {}),
26100
- /* @__PURE__ */ jsx(
26101
- SelectPrimitive.Viewport,
26102
- {
26103
- className: cn(
26104
- "p-1",
26105
- position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
26106
- ),
26107
- children
26108
- }
26109
- ),
26110
- /* @__PURE__ */ jsx(SelectScrollDownButton, {})
26111
- ]
26112
- }
26113
- ) }));
26114
- SelectContent.displayName = SelectPrimitive.Content.displayName;
26115
- var SelectLabel = React23.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
26116
- SelectPrimitive.Label,
26117
- {
26118
- ref,
26119
- className: cn("px-2 py-1.5 text-sm font-semibold", className),
26120
- ...props
26121
- }
26122
- ));
26123
- SelectLabel.displayName = SelectPrimitive.Label.displayName;
26124
- var SelectItem = React23.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
26125
- SelectPrimitive.Item,
26126
- {
26127
- ref,
26128
- className: cn(
26129
- "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
26130
- className
26131
- ),
26132
- ...props,
26133
- children: [
26134
- /* @__PURE__ */ jsx("span", { className: "absolute right-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx(Check, { className: "h-4 w-4" }) }) }),
26135
- /* @__PURE__ */ jsx(SelectPrimitive.ItemText, { children })
26136
- ]
26137
- }
26138
- ));
26139
- SelectItem.displayName = SelectPrimitive.Item.displayName;
26140
- var SelectSeparator = React23.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
26141
- SelectPrimitive.Separator,
26142
- {
26143
- ref,
26144
- className: cn("-mx-1 my-1 h-px bg-muted", className),
26145
- ...props
26146
- }
26147
- ));
26148
- SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
26149
- var LoadingOverlay = ({
26150
- isVisible,
26151
- message = "Loading...",
26152
- className
26153
- }) => {
26154
- if (!isVisible) return null;
26155
- return /* @__PURE__ */ jsx(
26156
- motion.div,
26157
- {
26158
- initial: { opacity: 0 },
26159
- animate: { opacity: 1 },
26160
- exit: { opacity: 0 },
26161
- transition: { duration: 0.2 },
26162
- className: `fixed inset-0 z-[100] flex items-center justify-center bg-black/30 backdrop-blur-sm ${className || ""}`,
26163
- "aria-modal": "true",
26164
- role: "dialog",
26165
- children: /* @__PURE__ */ jsx("div", { className: "flex flex-col items-center space-y-3 rounded-lg bg-white p-8 shadow-xl", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message }) })
26166
- }
26167
- );
26168
- };
26169
- var LoadingOverlay_default = LoadingOverlay;
26170
- var TimeDisplay = ({ className, variant = "default" }) => {
26171
- const { dateTimeConfig } = useDashboardConfig();
26172
- const [time2, setTime] = useState("");
26173
- const dbTimezone = useAppTimezone();
26174
- const timezoneToDisplay = dbTimezone || dateTimeConfig?.defaultTimezone || "UTC";
26175
- const localeToUse = dateTimeConfig?.defaultLocale || "en-US";
26176
- const timeSuffix = "";
26177
- useEffect(() => {
26178
- const updateTime = () => {
26179
- const now2 = /* @__PURE__ */ new Date();
26180
- const effectiveFormatOptions = {
26181
- hour: "2-digit",
26182
- minute: "2-digit",
26183
- second: "2-digit",
26184
- hour12: true,
26185
- timeZone: timezoneToDisplay,
26186
- ...dateTimeConfig?.timeFormatOptions || {}
26187
- // Allow override from config
26188
- };
26189
- try {
26190
- setTime(new Intl.DateTimeFormat(localeToUse, effectiveFormatOptions).format(now2));
26191
- } catch (e) {
26192
- console.error("Error formatting time:", e);
26193
- setTime("Error");
26194
- }
26195
- };
26196
- updateTime();
26197
- const interval = setInterval(updateTime, 1e3);
26198
- return () => clearInterval(interval);
26199
- }, [timezoneToDisplay, dateTimeConfig?.timeFormatOptions, localeToUse]);
26200
- if (!time2) return null;
26201
- if (variant === "minimal") {
26202
- return /* @__PURE__ */ jsxs("span", { className: className || "", children: [
26203
- time2,
26204
- " ",
26205
- timeSuffix
26206
- ] });
26207
- }
26208
- return /* @__PURE__ */ jsxs(
26209
- motion.div,
26210
- {
26211
- initial: { opacity: 0, y: -5 },
26212
- animate: { opacity: 1, y: 0 },
26213
- transition: { duration: 0.3 },
26214
- className: `flex items-center space-x-1.5 bg-white/60 backdrop-blur-sm px-2 py-0.5 rounded-md shadow-xs ${className || ""}`,
26215
- children: [
26216
- /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-3 w-3 text-[var(--primary-DEFAULT)]", viewBox: "0 0 20 20", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z", clipRule: "evenodd" }) }),
26217
- /* @__PURE__ */ jsxs("span", { className: "text-xs sm:text-[11px] font-medium text-gray-800 tabular-nums tracking-tight", children: [
26218
- time2,
26219
- " ",
26220
- timeSuffix
26221
- ] })
26222
- ]
26223
- }
26224
- );
26225
- };
26226
- var TimeDisplay_default = TimeDisplay;
26227
- var DateDisplay = ({ className, variant = "default" }) => {
26228
- const { dateTimeConfig } = useDashboardConfig();
26229
- const [date, setDate] = useState("");
26230
- const timezoneToDisplay = dateTimeConfig?.defaultTimezone || "UTC";
26231
- const localeToUse = dateTimeConfig?.defaultLocale || "en-US";
26232
- useEffect(() => {
26233
- const getCurrentFormattedDate = () => {
26234
- const now2 = /* @__PURE__ */ new Date();
26235
- const effectiveFormatOptions = {
26236
- weekday: "short",
26237
- day: "numeric",
26238
- month: "short",
26239
- timeZone: timezoneToDisplay,
26240
- ...dateTimeConfig?.dateFormatOptions || {}
26241
- // Allow override from config
26242
- };
26243
- try {
26244
- return new Intl.DateTimeFormat(localeToUse, effectiveFormatOptions).format(now2);
26245
- } catch (e) {
26246
- console.error("Error formatting date:", e);
26247
- return "Error";
26248
- }
26249
- };
26250
- const updateDate = () => {
26251
- setDate(getCurrentFormattedDate());
26252
- };
26253
- updateDate();
26254
- const interval = setInterval(() => {
26255
- const currentDateStr = getCurrentFormattedDate();
26256
- if (currentDateStr !== date) {
26257
- updateDate();
26258
- }
26259
- }, 60 * 1e3);
26260
- return () => clearInterval(interval);
26261
- }, [date, timezoneToDisplay, dateTimeConfig?.dateFormatOptions, localeToUse]);
26262
- if (!date) return null;
26263
- if (variant === "minimal") {
26264
- return /* @__PURE__ */ jsx("span", { className: className || "", children: date });
26265
- }
26266
- return /* @__PURE__ */ jsxs(
26267
- motion.div,
26268
- {
26269
- initial: { opacity: 0, y: -5 },
26270
- animate: { opacity: 1, y: 0 },
26271
- transition: { duration: 0.3 },
26272
- className: `flex items-center space-x-1.5 bg-white/60 backdrop-blur-sm px-2 py-0.5 rounded-md shadow-xs ${className || ""}`,
26273
- children: [
26274
- /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-3 w-3 text-blue-600", viewBox: "0 0 20 20", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M6 2a1 1 0 00-1 1v1H4a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-1V3a1 1 0 10-2 0v1H7V3a1 1 0 00-1-1zm0 5a1 1 0 000 2h8a1 1 0 100-2H6z", clipRule: "evenodd" }) }),
26275
- /* @__PURE__ */ jsx("span", { className: "text-[11px] font-medium text-gray-800 tracking-tight", children: date })
26276
- ]
26277
- }
26278
- );
26279
- };
26280
- var DateDisplay_default = DateDisplay;
26281
- var Card3 = Card2;
26282
- var CardHeader3 = CardHeader2;
26283
- var CardTitle3 = CardTitle2;
26284
- var CardContent3 = CardContent2;
26285
- var MetricCard2 = ({ title, value, unit = "", trend = null }) => {
26286
- const getTrendColor = (trendValue) => {
26287
- if (trendValue === null || trendValue === void 0) return "";
26288
- return trendValue > 0 ? "text-green-500" : trendValue < 0 ? "text-red-500" : "text-gray-500";
26289
- };
26290
- const getTrendSymbol = (trendValue) => {
26291
- if (trendValue === null || trendValue === void 0) return "";
26292
- return trendValue > 0 ? "\u2191" : trendValue < 0 ? "\u2193" : "";
26293
- };
26294
- return /* @__PURE__ */ jsxs(Card3, { className: "bg-white", children: [
26295
- /* @__PURE__ */ jsx(CardHeader3, { className: "pb-2", children: /* @__PURE__ */ jsx(CardTitle3, { className: "text-sm font-medium text-gray-500", children: title }) }),
26296
- /* @__PURE__ */ jsx(CardContent3, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between", children: [
26297
- /* @__PURE__ */ jsxs("div", { className: "text-2xl font-semibold", children: [
26298
- value,
26299
- unit && /* @__PURE__ */ jsx("span", { className: "ml-1 text-sm text-gray-500", children: unit })
26300
- ] }),
26301
- trend !== null && trend !== void 0 && // Check trend for null/undefined before accessing
26302
- /* @__PURE__ */ jsx("div", { className: `flex items-center ${getTrendColor(trend)}`, children: /* @__PURE__ */ jsxs("span", { className: "text-sm", children: [
26303
- getTrendSymbol(trend),
26304
- " ",
26305
- Math.abs(trend),
26306
- "%"
26307
- ] }) })
26308
- ] }) })
26309
- ] });
26310
- };
26311
- var MetricCard_default = MetricCard2;
26312
- var TimePickerDropdown = ({
26313
- value,
26314
- onChange,
26315
- placeholder = "Select time",
26316
- className = "",
26317
- disabled = false
26318
- }) => {
26319
- const [isOpen, setIsOpen] = useState(false);
26320
- const [searchTerm, setSearchTerm] = useState("");
26321
- const dropdownRef = useRef(null);
26322
- const inputRef = useRef(null);
26323
- const generateTimeSlots = () => {
26324
- const slots = [];
26325
- for (let hour = 0; hour < 24; hour++) {
26326
- for (let minute = 0; minute < 60; minute += 15) {
26327
- const time24 = `${hour.toString().padStart(2, "0")}:${minute.toString().padStart(2, "0")}`;
26328
- const hour12 = hour === 0 ? 12 : hour > 12 ? hour - 12 : hour;
26329
- const ampm = hour < 12 ? "AM" : "PM";
26330
- const time12 = `${hour12}:${minute.toString().padStart(2, "0")} ${ampm}`;
26331
- slots.push({ value: time24, label: time12 });
26332
- }
26333
- }
26334
- return slots;
26335
- };
26336
- const timeSlots = generateTimeSlots();
26337
- const filteredSlots = timeSlots.filter(
26338
- (slot) => slot.label.toLowerCase().includes(searchTerm.toLowerCase())
26339
- );
26340
- const getDisplayValue = (value2) => {
26341
- if (!value2) return "";
26342
- const normalizedValue = value2.substring(0, 5);
26343
- const slot = timeSlots.find((s) => s.value === normalizedValue);
26344
- return slot ? slot.label : value2;
26345
- };
26346
- useEffect(() => {
26347
- const handleClickOutside = (event) => {
26348
- if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
26349
- setIsOpen(false);
26350
- setSearchTerm("");
26351
- }
26352
- };
26353
- document.addEventListener("mousedown", handleClickOutside);
26354
- return () => document.removeEventListener("mousedown", handleClickOutside);
26355
- }, []);
26356
- const handleKeyDown = (e) => {
26357
- if (e.key === "Escape") {
26358
- setIsOpen(false);
26359
- setSearchTerm("");
26360
- } else if (e.key === "Enter") {
26361
- e.preventDefault();
26362
- if (filteredSlots.length > 0) {
26363
- onChange(filteredSlots[0].value);
26364
- setIsOpen(false);
26365
- setSearchTerm("");
26366
- }
26367
- }
26368
- };
26369
- const handleSelect = (timeValue) => {
26370
- onChange(timeValue);
26371
- setIsOpen(false);
26372
- setSearchTerm("");
26373
- };
26374
- const handleToggle = () => {
26375
- if (disabled) return;
26376
- setIsOpen(!isOpen);
26377
- if (!isOpen) {
26378
- setTimeout(() => inputRef.current?.focus(), 100);
26379
- }
26380
- };
26381
- return /* @__PURE__ */ jsxs("div", { className: `relative ${className}`, ref: dropdownRef, children: [
26382
- /* @__PURE__ */ jsx(
26383
- "button",
26384
- {
26385
- type: "button",
26386
- onClick: handleToggle,
26387
- disabled,
26388
- className: `
26389
- w-full px-3 py-2 text-left bg-white border border-gray-300 rounded-md shadow-sm
26390
- focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500
26391
- hover:border-gray-400 transition-colors duration-200
26392
- ${disabled ? "bg-gray-50 cursor-not-allowed" : "cursor-pointer"}
26393
- ${isOpen ? "ring-2 ring-blue-500 border-blue-500" : ""}
26394
- `,
26395
- children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
26396
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
26397
- /* @__PURE__ */ jsx(Clock, { className: "h-4 w-4 text-gray-400" }),
26398
- /* @__PURE__ */ jsx("span", { className: `text-sm ${value ? "text-gray-900" : "text-gray-500"}`, children: value ? getDisplayValue(value) : placeholder })
26399
- ] }),
26400
- /* @__PURE__ */ jsx(
26401
- ChevronDown,
26402
- {
26403
- className: `h-4 w-4 text-gray-400 transition-transform duration-200 ${isOpen ? "rotate-180" : ""}`
26404
- }
26405
- )
26406
- ] })
26407
- }
26408
- ),
26409
- isOpen && /* @__PURE__ */ jsxs("div", { className: "absolute z-50 w-full mt-1 bg-white border border-gray-300 rounded-md shadow-lg max-h-60 overflow-hidden", children: [
26410
- /* @__PURE__ */ jsx("div", { className: "p-2 border-b border-gray-200", children: /* @__PURE__ */ jsx(
26411
- "input",
26412
- {
26413
- ref: inputRef,
26414
- type: "text",
26415
- placeholder: "Search time...",
26416
- value: searchTerm,
26417
- onChange: (e) => setSearchTerm(e.target.value),
26418
- onKeyDown: handleKeyDown,
26419
- className: "w-full px-3 py-2 text-sm border border-gray-200 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
26420
- }
26421
- ) }),
26422
- /* @__PURE__ */ jsx("div", { className: "overflow-y-auto max-h-48", children: filteredSlots.length === 0 ? /* @__PURE__ */ jsx("div", { className: "px-3 py-2 text-sm text-gray-500 text-center", children: "No times found" }) : filteredSlots.map((slot) => /* @__PURE__ */ jsx(
26423
- "button",
26424
- {
26425
- type: "button",
26426
- onClick: () => handleSelect(slot.value),
26427
- className: `
26428
- w-full px-3 py-2 text-left text-sm hover:bg-blue-50 hover:text-blue-600
26429
- transition-colors duration-150 border-b border-gray-100 last:border-b-0
26430
- ${slot.value === value ? "bg-blue-50 text-blue-600 font-medium" : "text-gray-700"}
26431
- `,
26432
- children: slot.label
26433
- },
26434
- slot.value
26435
- )) })
26436
- ] })
26437
- ] });
26438
- };
26439
- var SilentErrorBoundary = class extends React23__default.Component {
26440
- constructor(props) {
26441
- super(props);
26442
- this.handleClearAndReload = () => {
26443
- console.log("[ErrorBoundary] User initiated reset");
26444
- if (typeof window !== "undefined") {
26445
- try {
26446
- localStorage.clear();
26447
- sessionStorage.clear();
26448
- console.log("[ErrorBoundary] Cleared all storage");
26449
- } catch (error) {
26450
- console.error("[ErrorBoundary] Failed to clear storage:", error);
26451
- }
26452
- }
26453
- window.location.href = "/login";
26454
- };
26455
- this.state = {
26456
- hasError: false,
26457
- errorCount: 0,
26458
- lastError: null,
26459
- errorInfo: null
26460
- };
26461
- }
26462
- static getDerivedStateFromError(error) {
26463
- return { hasError: true };
26464
- }
26465
- componentDidCatch(error, errorInfo) {
26466
- console.error("[ErrorBoundary] Caught render error:", {
26467
- error: error.message,
26468
- stack: error.stack,
26469
- componentStack: errorInfo.componentStack,
26470
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
26471
- });
26472
- this.setState((prev) => ({
26473
- errorCount: prev.errorCount + 1,
26474
- lastError: error,
26475
- errorInfo
26476
- }));
26477
- try {
26478
- if (typeof window !== "undefined" && window.mixpanel) {
26479
- window.mixpanel.track("React Render Error", {
26480
- error: error.message,
26481
- component: errorInfo.componentStack?.split("\n")[1] || "unknown",
26482
- errorCount: this.state.errorCount + 1
26483
- });
26484
- }
26485
- } catch (analyticsError) {
26486
- console.warn("[ErrorBoundary] Analytics tracking failed:", analyticsError);
26487
- }
26488
- }
26489
- render() {
26490
- if (!this.state.hasError) {
26491
- return this.props.children;
26492
- }
26493
- return /* @__PURE__ */ jsx("div", { className: "flex h-screen w-screen items-center justify-center bg-slate-50", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center space-y-6 text-center", children: [
26494
- /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "lg", message: "Loading Dashboard..." }),
26495
- /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500", children: "Taking longer than usual..." }),
26496
- /* @__PURE__ */ jsx("div", { className: "pt-4", children: /* @__PURE__ */ jsx(
26497
- "a",
26498
- {
26499
- href: "#",
26500
- onClick: (e) => {
26501
- e.preventDefault();
26502
- this.handleClearAndReload();
26503
- },
26504
- className: "text-xs text-gray-400 hover:text-gray-600 underline transition-colors",
26505
- children: "Reset and try again"
26506
- }
26507
- ) }),
26508
- process.env.NODE_ENV === "development" && /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-400 italic mt-4", children: "Check console for error details" })
26509
- ] }) });
26510
- }
26511
- };
26512
26037
  var PlayPauseIndicator = ({
26513
26038
  show,
26514
26039
  isPlaying,
@@ -26571,37 +26096,33 @@ var PlayPauseIndicator = ({
26571
26096
  );
26572
26097
  };
26573
26098
  var ERROR_MAPPING = {
26574
- 1: {
26575
- // MEDIA_ERR_ABORTED
26576
- code: 1,
26577
- type: "recoverable" /* RECOVERABLE */,
26578
- message: "Video loading was interrupted",
26579
- canRetry: true
26580
- },
26581
- 2: {
26582
- // MEDIA_ERR_NETWORK
26099
+ "networkError": {
26583
26100
  code: 2,
26584
- type: "recoverable" /* RECOVERABLE */,
26101
+ type: "recoverable",
26585
26102
  message: "Network error - please check your internet connection",
26586
26103
  canRetry: true
26587
26104
  },
26588
- 3: {
26589
- // MEDIA_ERR_DECODE
26105
+ "mediaError": {
26590
26106
  code: 3,
26591
- type: "non_recoverable" /* NON_RECOVERABLE */,
26107
+ type: "non_recoverable",
26592
26108
  message: "Stream corrupted due to internet connection",
26593
26109
  canRetry: false
26594
26110
  },
26595
- 4: {
26596
- // MEDIA_ERR_SRC_NOT_SUPPORTED
26111
+ "muxError": {
26112
+ code: 3,
26113
+ type: "non_recoverable",
26114
+ message: "Error processing media stream",
26115
+ canRetry: false
26116
+ },
26117
+ "otherError": {
26597
26118
  code: 4,
26598
- type: "non_recoverable" /* NON_RECOVERABLE */,
26119
+ type: "non_recoverable",
26599
26120
  message: "Video format not supported by your browser. Please use Google Chrome.",
26600
26121
  canRetry: false
26601
26122
  }
26602
26123
  };
26603
- var videoPlayerStyles = `
26604
- .video-player-container {
26124
+ var hlsVideoPlayerStyles = `
26125
+ .hls-video-player-container {
26605
26126
  width: 100%;
26606
26127
  height: 100%;
26607
26128
  background-color: #000;
@@ -26609,32 +26130,18 @@ var videoPlayerStyles = `
26609
26130
  align-items: center;
26610
26131
  justify-content: center;
26611
26132
  }
26612
-
26613
- /* Center the video player and maintain aspect ratio */
26614
- .video-js {
26133
+
26134
+ /* Center the video and maintain aspect ratio */
26135
+ .hls-video-element {
26615
26136
  width: 100%;
26616
26137
  height: 100%;
26617
26138
  max-width: 100%;
26618
26139
  max-height: 100%;
26619
- }
26620
-
26621
- /* Ensure video maintains aspect ratio and shows fully */
26622
- .video-js .vjs-tech {
26623
- position: absolute;
26624
- top: 0;
26625
- left: 0;
26626
- width: 100%;
26627
- height: 100%;
26628
26140
  object-fit: contain;
26629
26141
  }
26630
-
26631
- /* Hide default Video.js loading spinner */
26632
- .video-js .vjs-loading-spinner {
26633
- display: none !important;
26634
- }
26635
-
26142
+
26636
26143
  /* Custom loading indicator styles */
26637
- .video-player-loading {
26144
+ .hls-video-player-loading {
26638
26145
  position: absolute;
26639
26146
  top: 50%;
26640
26147
  left: 50%;
@@ -26643,15 +26150,41 @@ var videoPlayerStyles = `
26643
26150
  }
26644
26151
  `;
26645
26152
  if (typeof document !== "undefined") {
26646
- const styleId = "video-player-custom-styles";
26153
+ const styleId = "hls-video-player-custom-styles";
26647
26154
  if (!document.getElementById(styleId)) {
26648
26155
  const style = document.createElement("style");
26649
26156
  style.id = styleId;
26650
- style.textContent = videoPlayerStyles;
26157
+ style.textContent = hlsVideoPlayerStyles;
26651
26158
  document.head.appendChild(style);
26652
26159
  }
26653
26160
  }
26654
- var VideoPlayer = React23__default.forwardRef(({
26161
+ var BASE_HLS_CONFIG = {
26162
+ maxBufferLength: 3,
26163
+ maxMaxBufferLength: 8,
26164
+ maxBufferSize: 50 * 1e3 * 1e3,
26165
+ maxBufferHole: 0.25,
26166
+ manifestLoadingTimeOut: 15e3,
26167
+ manifestLoadingMaxRetry: 3,
26168
+ manifestLoadingRetryDelay: 500,
26169
+ levelLoadingTimeOut: 6e4,
26170
+ levelLoadingMaxRetry: 5,
26171
+ levelLoadingRetryDelay: 500,
26172
+ fragLoadingTimeOut: 6e4,
26173
+ fragLoadingMaxRetry: 5,
26174
+ fragLoadingRetryDelay: 500,
26175
+ startPosition: -1,
26176
+ debug: false,
26177
+ enableWorker: true,
26178
+ lowLatencyMode: false,
26179
+ progressive: true,
26180
+ abrEwmaSlowLive: 9,
26181
+ abrEwmaFastLive: 3,
26182
+ abrBandWidthFactor: 0.95,
26183
+ abrBandWidthUpFactor: 0.7,
26184
+ abrMaxWithRealBitrate: false,
26185
+ abrEwmaDefaultEstimate: 5e7
26186
+ };
26187
+ var HlsVideoPlayer = forwardRef(({
26655
26188
  src,
26656
26189
  poster,
26657
26190
  autoplay = false,
@@ -26660,7 +26193,9 @@ var VideoPlayer = React23__default.forwardRef(({
26660
26193
  muted = false,
26661
26194
  playsInline = true,
26662
26195
  className = "",
26196
+ hlsConfig = {},
26663
26197
  options = {},
26198
+ // Backward compatibility with Video.js
26664
26199
  externalLoadingControl = false,
26665
26200
  onLoadingChange,
26666
26201
  onReady,
@@ -26678,321 +26213,396 @@ var VideoPlayer = React23__default.forwardRef(({
26678
26213
  onSeeked,
26679
26214
  onClick
26680
26215
  }, ref) => {
26216
+ const videoContainerRef = useRef(null);
26681
26217
  const videoRef = useRef(null);
26682
- const playerRef = useRef(null);
26218
+ const hlsRef = useRef(null);
26219
+ const blobUrlRef = useRef(null);
26683
26220
  const [isReady, setIsReady] = useState(false);
26684
26221
  const [isLoading, setIsLoading] = useState(true);
26685
26222
  const [showIndicator, setShowIndicator] = useState(false);
26686
26223
  const [indicatorIsPlaying, setIndicatorIsPlaying] = useState(false);
26687
26224
  const indicatorKeyRef = useRef(0);
26688
- const defaultOptions = {
26689
- controls: false,
26690
- // Always disable Video.js controls - we use custom controls
26691
- // Don't use fluid or fill - let the video maintain its natural aspect ratio
26692
- fluid: false,
26693
- // Disable fluid mode to prevent stretching
26694
- responsive: false,
26695
- // Disable responsive mode
26696
- fill: false,
26697
- // Don't fill - maintain aspect ratio
26698
- playsinline: playsInline,
26699
- preload: "metadata",
26700
- autoplay: autoplay ? "any" : false,
26701
- muted,
26702
- loop,
26703
- poster,
26704
- // Optimized HLS configuration for VOD content with LARGE SEGMENTS (40MB)
26705
- // Strategy: Aggressive buffer reduction for faster startup with large segments
26706
- html5: {
26707
- vhs: {
26708
- // VHS (Video HTTP Streaming) options for HLS
26709
- withCredentials: false,
26710
- handleManifestRedirect: true,
26711
- overrideNative: !videojs.browser.IS_SAFARI,
26712
- // Use native HLS on Safari
26713
- enableLowInitialPlaylist: true,
26714
- smoothQualityChange: true,
26715
- // Optimized bandwidth and buffering for VOD
26716
- bandwidth: 5e7,
26717
- // Start with high bandwidth assumption (50 Mbps)
26718
- initialBandwidth: 5e7,
26719
- // Assume good connection initially
26720
- limitRenditionByPlayerDimensions: true,
26721
- // Buffer configuration optimized for large segments (40MB each)
26722
- maxBufferLength: 3,
26723
- // Start playing with just 3 seconds ahead
26724
- maxMaxBufferLength: 8,
26725
- // Maximum 8 seconds buffer for faster startup
26726
- maxBufferSize: 50 * 1e3 * 1e3,
26727
- // 50MB max buffer size (1.25 segments)
26728
- maxBufferHole: 0.25,
26729
- // Smaller holes for better continuity
26730
- bufferBasedABR: false,
26731
- // Disable for more predictable behavior
26732
- // Segment loading optimization for large segments (40MB)
26733
- maxPlaylistRetries: 3,
26734
- playlistRetryDelay: 500,
26735
- // 500ms between retries
26736
- playlistExclusionDuration: 60,
26737
- segmentLoadingRetryAttempts: 5,
26738
- // More retries for large segments
26739
- segmentLoadingRetryDelay: 500,
26740
- // Faster retry for responsiveness
26741
- segmentLoadingTimeOut: 6e4,
26742
- // 60s timeout for 40MB segments
26743
- manifestLoadingTimeOut: 15e3,
26744
- // 15s timeout for manifest
26745
- // Performance optimizations
26746
- experimentalBufferBasedCodecSwitching: true,
26747
- experimentalCacheEncryptionKeys: true,
26748
- handlePartialData: true,
26749
- allowSeeksWithinUnsafeLiveWindow: false,
26750
- // VOD content
26751
- experimentalLLHLS: false,
26752
- // Disable Low Latency HLS for VOD
26753
- // Connection settings
26754
- // Chrome 130+ started throwing "Cannot perform Construct on a detached ArrayBuffer"
26755
- // whenever the transmux worker tried to rehydrate transferred buffers that originated
26756
- // from Blob-based playlists. Disabling the worker keeps playback stable at the cost
26757
- // of slightly higher main-thread usage, which is acceptable for the dashboard usage.
26758
- enableWorker: false,
26759
- progressive: true,
26760
- // Progressive download
26761
- // Adaptive bitrate settings (if multi-quality available)
26762
- abrEwmaFastLive: 3,
26763
- abrEwmaSlowLive: 9,
26764
- abrBandWidthFactor: 0.95,
26765
- abrBandWidthUpFactor: 0.7,
26766
- abrMaxWithRealBitrate: false,
26767
- // Request options optimized for large segments
26768
- requestOptions: {
26769
- timeout: 9e4,
26770
- // 90s timeout for 40MB segments
26771
- maxRetry: 5
26772
- // More retries for reliability
26773
- }
26774
- },
26775
- nativeVideoTracks: false,
26776
- nativeAudioTracks: false,
26777
- nativeTextTracks: false
26778
- },
26779
- // Improved seeking and scrubbing
26780
- inactivityTimeout: 3e3,
26781
- // Better error handling
26782
- errorDisplay: false,
26783
- // We'll handle errors with callbacks
26784
- // Fullscreen options
26785
- fullscreen: {
26786
- options: {
26787
- navigationUI: "hide"
26788
- }
26789
- },
26790
- ...options
26791
- };
26792
- const initializePlayer = useCallback(() => {
26793
- if (!videoRef.current || playerRef.current) return;
26794
- const videoElement = document.createElement("video-js");
26795
- videoElement.className = "vjs-default-skin";
26796
- videoRef.current.appendChild(videoElement);
26797
- const player = videojs(videoElement, defaultOptions);
26798
- playerRef.current = player;
26799
- player.ready(() => {
26800
- setIsReady(true);
26801
- onReady?.(player);
26802
- });
26803
- player.on("play", () => onPlay?.(player));
26804
- player.on("pause", () => onPause?.(player));
26805
- player.on("playing", () => onPlaying?.(player));
26806
- player.on("timeupdate", () => {
26807
- const currentTime2 = player.currentTime() || 0;
26808
- onTimeUpdate?.(player, currentTime2);
26809
- });
26810
- player.on("durationchange", () => {
26811
- const duration2 = player.duration() || 0;
26812
- onDurationChange?.(player, duration2);
26813
- });
26814
- player.on("ended", () => onEnded?.(player));
26815
- player.on("loadstart", () => {
26816
- setIsLoading(true);
26817
- onLoadingChange?.(true);
26818
- onLoadStart?.(player);
26819
- });
26820
- player.on("loadeddata", () => {
26821
- setIsLoading(false);
26822
- onLoadingChange?.(false);
26823
- onLoadedData?.(player);
26824
- });
26825
- player.on("waiting", () => {
26826
- setIsLoading(true);
26827
- onLoadingChange?.(true);
26828
- });
26829
- player.on("playing", () => {
26830
- setIsLoading(false);
26831
- onLoadingChange?.(false);
26832
- });
26833
- player.on("loadedmetadata", () => {
26834
- onLoadedMetadata?.(player);
26835
- });
26836
- player.on("seeking", () => onSeeking?.(player));
26837
- player.on("seeked", () => onSeeked?.(player));
26838
- player.on("error", () => {
26839
- const error = player.error();
26840
- const errorCode = error?.code ?? 0;
26841
- const errorInfo = ERROR_MAPPING[errorCode] || {
26842
- code: errorCode || 0,
26843
- type: "non_recoverable" /* NON_RECOVERABLE */,
26844
- message: "Unknown playback error occurred",
26845
- canRetry: false
26846
- };
26847
- console.error("[VideoPlayer] Video.js error:", {
26848
- code: errorCode,
26849
- type: errorInfo.type,
26850
- message: errorInfo.message,
26851
- canRetry: errorInfo.canRetry,
26852
- originalError: error
26853
- });
26854
- onError?.(player, errorInfo);
26855
- });
26856
- if (src) {
26857
- const isHLS = src.endsWith(".m3u8") || src.startsWith("#EXTM3U");
26858
- let videoSrc = src;
26859
- let blobUrl = null;
26860
- if (src.startsWith("#EXTM3U")) {
26861
- const safariMode = isSafari();
26862
- const browserName = getBrowserName();
26863
- if (safariMode) {
26864
- const clipIdMatch = src.match(/# Clip ID: ([a-f0-9-]+)/i);
26865
- if (clipIdMatch) {
26866
- videoSrc = `/api/clips/playlist/${clipIdMatch[1]}`;
26867
- console.log(`[VideoPlayer] Safari detected (${browserName}) - using playlist proxy URL:`, videoSrc);
26868
- } else {
26869
- console.warn("[VideoPlayer] Safari detected but no clip ID found in playlist, trying blob URL fallback");
26870
- const blob = new Blob([src], { type: "application/vnd.apple.mpegurl" });
26871
- blobUrl = URL.createObjectURL(blob);
26872
- videoSrc = blobUrl;
26873
- player._blobUrl = videoSrc;
26874
- }
26875
- } else {
26876
- const blob = new Blob([src], { type: "application/vnd.apple.mpegurl" });
26877
- blobUrl = URL.createObjectURL(blob);
26878
- videoSrc = blobUrl;
26879
- console.log(`[VideoPlayer] Non-Safari browser (${browserName}) - using blob URL for optimal performance`);
26880
- player._blobUrl = videoSrc;
26881
- }
26882
- }
26883
- player.src({
26884
- src: videoSrc,
26885
- type: isHLS ? "application/x-mpegURL" : "video/mp4"
26886
- });
26887
- }
26225
+ const eventCallbacksRef = useRef({
26226
+ onReady,
26227
+ onPlay,
26228
+ onPause,
26229
+ onPlaying,
26230
+ onTimeUpdate,
26231
+ onDurationChange,
26232
+ onEnded,
26233
+ onError,
26234
+ onLoadStart,
26235
+ onLoadedMetadata,
26236
+ onLoadedData,
26237
+ onSeeking,
26238
+ onSeeked,
26239
+ onLoadingChange
26240
+ });
26241
+ useEffect(() => {
26242
+ eventCallbacksRef.current = {
26243
+ onReady,
26244
+ onPlay,
26245
+ onPause,
26246
+ onPlaying,
26247
+ onTimeUpdate,
26248
+ onDurationChange,
26249
+ onEnded,
26250
+ onError,
26251
+ onLoadStart,
26252
+ onLoadedMetadata,
26253
+ onLoadedData,
26254
+ onSeeking,
26255
+ onSeeked,
26256
+ onLoadingChange
26257
+ };
26888
26258
  }, [
26889
- src,
26890
- defaultOptions,
26891
26259
  onReady,
26892
26260
  onPlay,
26893
26261
  onPause,
26262
+ onPlaying,
26894
26263
  onTimeUpdate,
26895
26264
  onDurationChange,
26896
26265
  onEnded,
26897
26266
  onError,
26898
26267
  onLoadStart,
26899
26268
  onLoadedMetadata,
26269
+ onLoadedData,
26900
26270
  onSeeking,
26901
- onSeeked
26271
+ onSeeked,
26272
+ onLoadingChange
26902
26273
  ]);
26274
+ const stableHlsConfigRef = useRef(hlsConfig);
26275
+ const stableOptionsRef = useRef(options);
26276
+ const configSignatureRef = useRef("");
26277
+ const [configVersion, setConfigVersion] = useState(0);
26903
26278
  useEffect(() => {
26904
- if (playerRef.current && src) {
26905
- const isHLS = src.endsWith(".m3u8") || src.startsWith("#EXTM3U");
26906
- let videoSrc = src;
26907
- let blobUrl = null;
26279
+ const serialized = JSON.stringify({
26280
+ hlsConfig: hlsConfig || null,
26281
+ options: options || null
26282
+ });
26283
+ if (!configSignatureRef.current) {
26284
+ configSignatureRef.current = serialized;
26285
+ stableHlsConfigRef.current = hlsConfig;
26286
+ stableOptionsRef.current = options;
26287
+ return;
26288
+ }
26289
+ if (configSignatureRef.current !== serialized) {
26290
+ configSignatureRef.current = serialized;
26291
+ stableHlsConfigRef.current = hlsConfig;
26292
+ stableOptionsRef.current = options;
26293
+ setConfigVersion((prev) => prev + 1);
26294
+ }
26295
+ }, [hlsConfig, options]);
26296
+ const cleanupBlobUrl = useCallback(() => {
26297
+ if (blobUrlRef.current) {
26298
+ URL.revokeObjectURL(blobUrlRef.current);
26299
+ blobUrlRef.current = null;
26300
+ }
26301
+ }, []);
26302
+ const dispose = useCallback(() => {
26303
+ if (hlsRef.current) {
26304
+ hlsRef.current.destroy();
26305
+ hlsRef.current = null;
26306
+ }
26307
+ cleanupBlobUrl();
26308
+ setIsReady(false);
26309
+ }, [cleanupBlobUrl]);
26310
+ const playerLikeObject = useCallback(() => {
26311
+ return {
26312
+ el: () => videoRef.current,
26313
+ currentTime: () => videoRef.current?.currentTime || 0,
26314
+ duration: () => videoRef.current?.duration || 0,
26315
+ paused: () => videoRef.current?.paused ?? true,
26316
+ play: () => videoRef.current?.play(),
26317
+ pause: () => videoRef.current?.pause(),
26318
+ muted: (val) => {
26319
+ if (videoRef.current) {
26320
+ if (val !== void 0) videoRef.current.muted = val;
26321
+ return videoRef.current.muted;
26322
+ }
26323
+ return false;
26324
+ },
26325
+ volume: (val) => {
26326
+ if (videoRef.current) {
26327
+ if (val !== void 0) videoRef.current.volume = val;
26328
+ return videoRef.current.volume;
26329
+ }
26330
+ return 1;
26331
+ },
26332
+ error: () => null,
26333
+ dispose: () => dispose()
26334
+ };
26335
+ }, [dispose]);
26336
+ const initializePlayer = useCallback(() => {
26337
+ if (!videoRef.current || !src) return;
26338
+ const video = videoRef.current;
26339
+ const player = playerLikeObject();
26340
+ const mergedHlsConfig = {
26341
+ ...BASE_HLS_CONFIG,
26342
+ ...stableHlsConfigRef.current || {},
26343
+ ...stableOptionsRef.current || {}
26344
+ };
26345
+ cleanupBlobUrl();
26346
+ const isHLS = src.endsWith(".m3u8") || src.startsWith("#EXTM3U");
26347
+ if (isHLS) {
26908
26348
  if (src.startsWith("#EXTM3U")) {
26909
26349
  const safariMode = isSafari();
26910
26350
  const browserName = getBrowserName();
26911
26351
  if (safariMode) {
26912
26352
  const clipIdMatch = src.match(/# Clip ID: ([a-f0-9-]+)/i);
26913
26353
  if (clipIdMatch) {
26914
- videoSrc = `/api/clips/playlist/${clipIdMatch[1]}`;
26915
- console.log(`[VideoPlayer] Safari detected (${browserName}) - using playlist proxy URL for source update:`, videoSrc);
26354
+ const proxyUrl = `/api/clips/playlist/${clipIdMatch[1]}`;
26355
+ console.log(`[HlsVideoPlayer] Safari detected (${browserName}) - using playlist proxy URL:`, proxyUrl);
26356
+ video.src = proxyUrl;
26916
26357
  } else {
26917
- console.warn("[VideoPlayer] Safari detected but no clip ID found in playlist (source update), trying blob URL fallback");
26358
+ console.warn("[HlsVideoPlayer] Safari detected but no clip ID found in playlist, trying blob URL fallback");
26918
26359
  const blob = new Blob([src], { type: "application/vnd.apple.mpegurl" });
26919
- blobUrl = URL.createObjectURL(blob);
26920
- videoSrc = blobUrl;
26360
+ const blobUrl = URL.createObjectURL(blob);
26361
+ blobUrlRef.current = blobUrl;
26362
+ video.src = blobUrl;
26921
26363
  }
26922
- } else {
26364
+ } else if (Hls3.isSupported()) {
26923
26365
  const blob = new Blob([src], { type: "application/vnd.apple.mpegurl" });
26924
- blobUrl = URL.createObjectURL(blob);
26925
- videoSrc = blobUrl;
26926
- console.log(`[VideoPlayer] Non-Safari browser (${browserName}) - using blob URL for source update`);
26366
+ const blobUrl = URL.createObjectURL(blob);
26367
+ blobUrlRef.current = blobUrl;
26368
+ console.log(`[HlsVideoPlayer] Non-Safari browser (${browserName}) - using HLS.js with blob URL`);
26369
+ const hls = new Hls3(mergedHlsConfig);
26370
+ hlsRef.current = hls;
26371
+ hls.on(Events.MANIFEST_PARSED, () => {
26372
+ console.log("[HlsVideoPlayer] Manifest parsed, ready to play");
26373
+ setIsReady(true);
26374
+ eventCallbacksRef.current.onReady?.(player);
26375
+ });
26376
+ hls.on(Events.ERROR, (event, data) => {
26377
+ console.error("[HlsVideoPlayer] HLS.js error:", data);
26378
+ if (data.fatal) {
26379
+ let errorInfo;
26380
+ switch (data.type) {
26381
+ case ErrorTypes.NETWORK_ERROR:
26382
+ errorInfo = ERROR_MAPPING.networkError;
26383
+ console.log("[HlsVideoPlayer] Attempting to recover from network error");
26384
+ hls.startLoad();
26385
+ break;
26386
+ case ErrorTypes.MEDIA_ERROR:
26387
+ errorInfo = ERROR_MAPPING.mediaError;
26388
+ console.log("[HlsVideoPlayer] Attempting to recover from media error");
26389
+ hls.recoverMediaError();
26390
+ break;
26391
+ case ErrorTypes.MUX_ERROR:
26392
+ errorInfo = ERROR_MAPPING.muxError;
26393
+ break;
26394
+ default:
26395
+ errorInfo = ERROR_MAPPING.otherError;
26396
+ break;
26397
+ }
26398
+ errorInfo.details = data.details;
26399
+ eventCallbacksRef.current.onError?.(player, errorInfo);
26400
+ }
26401
+ });
26402
+ hls.on(Events.FRAG_LOADING, () => {
26403
+ setIsLoading(true);
26404
+ eventCallbacksRef.current.onLoadingChange?.(true);
26405
+ });
26406
+ hls.on(Events.FRAG_LOADED, () => {
26407
+ setIsLoading(false);
26408
+ eventCallbacksRef.current.onLoadingChange?.(false);
26409
+ });
26410
+ hls.loadSource(blobUrl);
26411
+ hls.attachMedia(video);
26412
+ } else {
26413
+ console.error("[HlsVideoPlayer] HLS.js not supported and not Safari - cannot play HLS content");
26414
+ const errorInfo = ERROR_MAPPING.otherError;
26415
+ onError?.(player, errorInfo);
26927
26416
  }
26928
- }
26929
- playerRef.current.src({
26930
- src: videoSrc,
26931
- type: isHLS ? "application/x-mpegURL" : "video/mp4"
26932
- });
26933
- return () => {
26934
- if (blobUrl) {
26935
- URL.revokeObjectURL(blobUrl);
26417
+ } else {
26418
+ if (Hls3.isSupported() && !isSafari()) {
26419
+ const hls = new Hls3(mergedHlsConfig);
26420
+ hlsRef.current = hls;
26421
+ hls.on(Events.MANIFEST_PARSED, () => {
26422
+ setIsReady(true);
26423
+ eventCallbacksRef.current.onReady?.(player);
26424
+ });
26425
+ hls.on(Events.ERROR, (event, data) => {
26426
+ console.error("[HlsVideoPlayer] HLS.js error:", data);
26427
+ if (data.fatal) {
26428
+ let errorInfo;
26429
+ switch (data.type) {
26430
+ case ErrorTypes.NETWORK_ERROR:
26431
+ errorInfo = ERROR_MAPPING.networkError;
26432
+ hls.startLoad();
26433
+ break;
26434
+ case ErrorTypes.MEDIA_ERROR:
26435
+ errorInfo = ERROR_MAPPING.mediaError;
26436
+ hls.recoverMediaError();
26437
+ break;
26438
+ default:
26439
+ errorInfo = ERROR_MAPPING.otherError;
26440
+ break;
26441
+ }
26442
+ errorInfo.details = data.details;
26443
+ eventCallbacksRef.current.onError?.(player, errorInfo);
26444
+ }
26445
+ });
26446
+ hls.loadSource(src);
26447
+ hls.attachMedia(video);
26448
+ } else {
26449
+ video.src = src;
26936
26450
  }
26937
- };
26451
+ }
26452
+ } else {
26453
+ video.src = src;
26938
26454
  }
26939
- }, [src]);
26455
+ const handleCanPlay = () => {
26456
+ if (!hlsRef.current) {
26457
+ setIsReady(true);
26458
+ onReady?.(player);
26459
+ }
26460
+ };
26461
+ const handlePlay = () => eventCallbacksRef.current.onPlay?.(player);
26462
+ const handlePause = () => eventCallbacksRef.current.onPause?.(player);
26463
+ const handlePlaying = () => {
26464
+ setIsLoading(false);
26465
+ eventCallbacksRef.current.onLoadingChange?.(false);
26466
+ eventCallbacksRef.current.onPlaying?.(player);
26467
+ };
26468
+ const handleTimeUpdate = () => {
26469
+ const currentTime2 = video.currentTime || 0;
26470
+ eventCallbacksRef.current.onTimeUpdate?.(player, currentTime2);
26471
+ };
26472
+ const handleDurationChange = () => {
26473
+ const duration2 = video.duration || 0;
26474
+ eventCallbacksRef.current.onDurationChange?.(player, duration2);
26475
+ };
26476
+ const handleEnded = () => eventCallbacksRef.current.onEnded?.(player);
26477
+ const handleLoadStart = () => {
26478
+ setIsLoading(true);
26479
+ eventCallbacksRef.current.onLoadingChange?.(true);
26480
+ eventCallbacksRef.current.onLoadStart?.(player);
26481
+ };
26482
+ const handleLoadedMetadata = () => eventCallbacksRef.current.onLoadedMetadata?.(player);
26483
+ const handleLoadedData = () => {
26484
+ setIsLoading(false);
26485
+ eventCallbacksRef.current.onLoadingChange?.(false);
26486
+ eventCallbacksRef.current.onLoadedData?.(player);
26487
+ };
26488
+ const handleWaiting = () => {
26489
+ setIsLoading(true);
26490
+ eventCallbacksRef.current.onLoadingChange?.(true);
26491
+ };
26492
+ const handleSeeking = () => eventCallbacksRef.current.onSeeking?.(player);
26493
+ const handleSeeked = () => eventCallbacksRef.current.onSeeked?.(player);
26494
+ const handleError = () => {
26495
+ const error = video.error;
26496
+ if (error) {
26497
+ const errorInfo = {
26498
+ code: error.code,
26499
+ type: error.code <= 2 ? "recoverable" : "non_recoverable",
26500
+ message: error.message || "Unknown error occurred",
26501
+ canRetry: error.code <= 2
26502
+ };
26503
+ eventCallbacksRef.current.onError?.(player, errorInfo);
26504
+ }
26505
+ };
26506
+ video.addEventListener("canplay", handleCanPlay);
26507
+ video.addEventListener("play", handlePlay);
26508
+ video.addEventListener("pause", handlePause);
26509
+ video.addEventListener("playing", handlePlaying);
26510
+ video.addEventListener("timeupdate", handleTimeUpdate);
26511
+ video.addEventListener("durationchange", handleDurationChange);
26512
+ video.addEventListener("ended", handleEnded);
26513
+ video.addEventListener("loadstart", handleLoadStart);
26514
+ video.addEventListener("loadedmetadata", handleLoadedMetadata);
26515
+ video.addEventListener("loadeddata", handleLoadedData);
26516
+ video.addEventListener("waiting", handleWaiting);
26517
+ video.addEventListener("seeking", handleSeeking);
26518
+ video.addEventListener("seeked", handleSeeked);
26519
+ video.addEventListener("error", handleError);
26520
+ return () => {
26521
+ video.removeEventListener("canplay", handleCanPlay);
26522
+ video.removeEventListener("play", handlePlay);
26523
+ video.removeEventListener("pause", handlePause);
26524
+ video.removeEventListener("playing", handlePlaying);
26525
+ video.removeEventListener("timeupdate", handleTimeUpdate);
26526
+ video.removeEventListener("durationchange", handleDurationChange);
26527
+ video.removeEventListener("ended", handleEnded);
26528
+ video.removeEventListener("loadstart", handleLoadStart);
26529
+ video.removeEventListener("loadedmetadata", handleLoadedMetadata);
26530
+ video.removeEventListener("loadeddata", handleLoadedData);
26531
+ video.removeEventListener("waiting", handleWaiting);
26532
+ video.removeEventListener("seeking", handleSeeking);
26533
+ video.removeEventListener("seeked", handleSeeked);
26534
+ video.removeEventListener("error", handleError);
26535
+ };
26536
+ }, [
26537
+ src,
26538
+ cleanupBlobUrl,
26539
+ playerLikeObject,
26540
+ configVersion
26541
+ ]);
26940
26542
  useEffect(() => {
26941
- initializePlayer();
26543
+ const cleanup = initializePlayer();
26942
26544
  return () => {
26943
- if (playerRef.current) {
26944
- const blobUrl = playerRef.current._blobUrl;
26945
- if (blobUrl) {
26946
- URL.revokeObjectURL(blobUrl);
26947
- }
26948
- playerRef.current.dispose();
26949
- playerRef.current = null;
26950
- setIsReady(false);
26545
+ cleanup?.();
26546
+ if (hlsRef.current) {
26547
+ hlsRef.current.destroy();
26548
+ hlsRef.current = null;
26951
26549
  }
26550
+ cleanupBlobUrl();
26551
+ setIsReady(false);
26952
26552
  };
26953
- }, []);
26553
+ }, [src, initializePlayer, cleanupBlobUrl]);
26554
+ useEffect(() => {
26555
+ if (videoRef.current) {
26556
+ if (autoplay) {
26557
+ videoRef.current.play().catch((err) => {
26558
+ console.warn("[HlsVideoPlayer] Autoplay failed:", err);
26559
+ });
26560
+ }
26561
+ }
26562
+ }, [autoplay]);
26954
26563
  const play = useCallback(() => {
26955
- return playerRef.current?.play();
26564
+ return videoRef.current?.play();
26956
26565
  }, []);
26957
26566
  const pause = useCallback(() => {
26958
- playerRef.current?.pause();
26567
+ videoRef.current?.pause();
26959
26568
  }, []);
26960
26569
  const currentTime = useCallback((time2) => {
26961
- if (time2 !== void 0) {
26962
- playerRef.current?.currentTime(time2);
26570
+ if (time2 !== void 0 && videoRef.current) {
26571
+ videoRef.current.currentTime = time2;
26963
26572
  return time2;
26964
26573
  }
26965
- return playerRef.current?.currentTime() || 0;
26574
+ return videoRef.current?.currentTime || 0;
26966
26575
  }, []);
26967
26576
  const duration = useCallback(() => {
26968
- return playerRef.current?.duration() || 0;
26577
+ return videoRef.current?.duration || 0;
26969
26578
  }, []);
26970
26579
  const paused = useCallback(() => {
26971
- return playerRef.current?.paused() ?? true;
26580
+ return videoRef.current?.paused ?? true;
26972
26581
  }, []);
26973
26582
  const mute = useCallback((isMuted) => {
26974
- if (isMuted !== void 0) {
26975
- playerRef.current?.muted(isMuted);
26583
+ if (isMuted !== void 0 && videoRef.current) {
26584
+ videoRef.current.muted = isMuted;
26976
26585
  return isMuted;
26977
26586
  }
26978
- return playerRef.current?.muted() ?? false;
26587
+ return videoRef.current?.muted ?? false;
26979
26588
  }, []);
26980
26589
  const volume = useCallback((level) => {
26981
- if (level !== void 0) {
26982
- playerRef.current?.volume(level);
26590
+ if (level !== void 0 && videoRef.current) {
26591
+ videoRef.current.volume = level;
26983
26592
  return level;
26984
26593
  }
26985
- return playerRef.current?.volume() ?? 1;
26594
+ return videoRef.current?.volume ?? 1;
26986
26595
  }, []);
26987
- const dispose = useCallback(() => {
26988
- if (playerRef.current) {
26989
- playerRef.current.dispose();
26990
- playerRef.current = null;
26991
- setIsReady(false);
26596
+ const playbackRate = useCallback((rate) => {
26597
+ if (rate !== void 0 && videoRef.current) {
26598
+ videoRef.current.playbackRate = rate;
26599
+ return rate;
26992
26600
  }
26601
+ return videoRef.current?.playbackRate ?? 1;
26993
26602
  }, []);
26994
- React23__default.useImperativeHandle(ref, () => ({
26995
- player: playerRef.current,
26603
+ useImperativeHandle(ref, () => ({
26604
+ hls: hlsRef.current,
26605
+ video: videoRef.current,
26996
26606
  play,
26997
26607
  pause,
26998
26608
  currentTime,
@@ -27000,13 +26610,15 @@ var VideoPlayer = React23__default.forwardRef(({
27000
26610
  paused,
27001
26611
  mute,
27002
26612
  volume,
26613
+ playbackRate,
27003
26614
  dispose,
27004
- isReady
27005
- }));
26615
+ isReady,
26616
+ // For backward compatibility with Video.js API
26617
+ player: playerLikeObject()
26618
+ }), [play, pause, currentTime, duration, paused, mute, volume, playbackRate, dispose, isReady, playerLikeObject]);
27006
26619
  const handleClickWithIndicator = useCallback(() => {
27007
- if (!onClick || !playerRef.current) return;
27008
- const player = playerRef.current;
27009
- const willBePlaying = player.paused();
26620
+ if (!onClick || !videoRef.current) return;
26621
+ const willBePlaying = videoRef.current.paused;
27010
26622
  setIndicatorIsPlaying(willBePlaying);
27011
26623
  setShowIndicator(false);
27012
26624
  setTimeout(() => {
@@ -27015,17 +26627,30 @@ var VideoPlayer = React23__default.forwardRef(({
27015
26627
  }, 0);
27016
26628
  onClick();
27017
26629
  }, [onClick]);
27018
- return /* @__PURE__ */ jsxs("div", { className: `video-player-wrapper ${className}`, style: { position: "relative", width: "100%", height: "100%" }, children: [
26630
+ return /* @__PURE__ */ jsxs("div", { className: `hls-video-player-wrapper ${className}`, style: { position: "relative", width: "100%", height: "100%" }, children: [
27019
26631
  /* @__PURE__ */ jsx(
27020
26632
  "div",
27021
26633
  {
27022
- className: "video-player-container",
27023
- ref: videoRef,
27024
- "data-vjs-player": true
26634
+ className: "hls-video-player-container",
26635
+ ref: videoContainerRef,
26636
+ children: /* @__PURE__ */ jsx(
26637
+ "video",
26638
+ {
26639
+ ref: videoRef,
26640
+ className: "hls-video-element",
26641
+ poster,
26642
+ controls,
26643
+ loop,
26644
+ muted,
26645
+ playsInline,
26646
+ autoPlay: autoplay,
26647
+ preload: "metadata"
26648
+ }
26649
+ )
27025
26650
  }
27026
26651
  ),
27027
- isLoading && !externalLoadingControl && /* @__PURE__ */ jsx("div", { className: "video-player-loading", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
27028
- onClick && /* @__PURE__ */ jsx(
26652
+ isLoading && !externalLoadingControl && /* @__PURE__ */ jsx("div", { className: "hls-video-player-loading", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
26653
+ onClick && !controls && /* @__PURE__ */ jsx(
27029
26654
  "div",
27030
26655
  {
27031
26656
  onClick: handleClickWithIndicator,
@@ -27041,7 +26666,7 @@ var VideoPlayer = React23__default.forwardRef(({
27041
26666
  "aria-label": "Click to play/pause"
27042
26667
  }
27043
26668
  ),
27044
- onClick && /* @__PURE__ */ jsx(
26669
+ onClick && !controls && /* @__PURE__ */ jsx(
27045
26670
  PlayPauseIndicator,
27046
26671
  {
27047
26672
  show: showIndicator,
@@ -27051,13 +26676,25 @@ var VideoPlayer = React23__default.forwardRef(({
27051
26676
  )
27052
26677
  ] });
27053
26678
  });
27054
- VideoPlayer.displayName = "VideoPlayer";
27055
- var CroppedVideoPlayer = forwardRef(({
26679
+ HlsVideoPlayer.displayName = "HlsVideoPlayer";
26680
+ var VideoPlayer = HlsVideoPlayer;
26681
+ var CroppedHlsVideoPlayer = forwardRef(({
27056
26682
  crop,
27057
26683
  debug = false,
27058
26684
  onClick,
27059
26685
  ...videoProps
27060
26686
  }, ref) => {
26687
+ const {
26688
+ onReady: onReadyProp,
26689
+ onPlay: onPlayProp,
26690
+ onPause: onPauseProp,
26691
+ onEnded: onEndedProp,
26692
+ onSeeking: onSeekingProp,
26693
+ onSeeked: onSeekedProp,
26694
+ onLoadedMetadata: onLoadedMetadataProp,
26695
+ className: inheritedClassName = ""
26696
+ } = videoProps;
26697
+ const videoSrc = videoProps.src;
27061
26698
  const videoContainerRef = useRef(null);
27062
26699
  const hiddenVideoRef = useRef(null);
27063
26700
  const canvasRef = useRef(null);
@@ -27076,8 +26713,11 @@ var CroppedVideoPlayer = forwardRef(({
27076
26713
  }
27077
26714
  }, []);
27078
26715
  useImperativeHandle(ref, () => ({
27079
- get player() {
27080
- return hiddenVideoRef.current?.player || null;
26716
+ get hls() {
26717
+ return hiddenVideoRef.current?.hls || null;
26718
+ },
26719
+ get video() {
26720
+ return hiddenVideoRef.current?.video || null;
27081
26721
  },
27082
26722
  play: () => hiddenVideoRef.current?.play() || void 0,
27083
26723
  pause: () => hiddenVideoRef.current?.pause(),
@@ -27091,12 +26731,36 @@ var CroppedVideoPlayer = forwardRef(({
27091
26731
  paused: () => hiddenVideoRef.current?.paused() || true,
27092
26732
  mute: (isMuted) => hiddenVideoRef.current?.mute(isMuted) || false,
27093
26733
  volume: (level) => hiddenVideoRef.current?.volume(level) || 0,
26734
+ playbackRate: (rate) => hiddenVideoRef.current?.playbackRate(rate) || 1,
27094
26735
  dispose: () => {
27095
26736
  hiddenVideoRef.current?.dispose();
27096
26737
  stopCanvasRendering();
27097
26738
  },
27098
26739
  get isReady() {
27099
26740
  return hiddenVideoRef.current?.isReady || false;
26741
+ },
26742
+ // For backward compatibility with Video.js API
26743
+ get player() {
26744
+ const video = hiddenVideoRef.current?.video;
26745
+ if (!video) return null;
26746
+ return {
26747
+ el: () => video,
26748
+ currentTime: () => video.currentTime || 0,
26749
+ duration: () => video.duration || 0,
26750
+ paused: () => video.paused ?? true,
26751
+ play: () => video.play(),
26752
+ pause: () => video.pause(),
26753
+ muted: (val) => {
26754
+ if (val !== void 0) video.muted = val;
26755
+ return video.muted;
26756
+ },
26757
+ volume: (val) => {
26758
+ if (val !== void 0) video.volume = val;
26759
+ return video.volume;
26760
+ },
26761
+ error: () => null,
26762
+ dispose: () => hiddenVideoRef.current?.dispose()
26763
+ };
27100
26764
  }
27101
26765
  }), [stopCanvasRendering]);
27102
26766
  const calculateCanvasDimensions = useCallback(() => {
@@ -27156,24 +26820,24 @@ var CroppedVideoPlayer = forwardRef(({
27156
26820
  animationFrameRef.current = requestAnimationFrame(renderFrameToCanvas);
27157
26821
  }, [crop]);
27158
26822
  const handleVideoReady = useCallback((player) => {
27159
- console.log("[CroppedVideoPlayer] Video player ready");
27160
- const videoEl = player.el().querySelector("video");
26823
+ console.log("[CroppedHlsVideoPlayer] Video player ready");
26824
+ const videoEl = hiddenVideoRef.current?.video;
27161
26825
  if (videoEl) {
27162
26826
  videoElementRef.current = videoEl;
27163
26827
  setIsVideoReady(true);
27164
26828
  }
27165
- videoProps.onReady?.(player);
27166
- }, [videoProps]);
26829
+ onReadyProp?.(player);
26830
+ }, [onReadyProp]);
27167
26831
  const handleVideoPlay = useCallback((player) => {
27168
- console.log("[CroppedVideoPlayer] Video playing, starting canvas rendering");
26832
+ console.log("[CroppedHlsVideoPlayer] Video playing, starting canvas rendering");
27169
26833
  if (crop && canvasRef.current) {
27170
26834
  setIsProcessing(true);
27171
26835
  renderFrameToCanvas();
27172
26836
  }
27173
- videoProps.onPlay?.(player);
27174
- }, [crop, renderFrameToCanvas, videoProps]);
26837
+ onPlayProp?.(player);
26838
+ }, [crop, renderFrameToCanvas, onPlayProp]);
27175
26839
  const handleVideoPause = useCallback((player) => {
27176
- console.log("[CroppedVideoPlayer] Video paused, stopping canvas rendering and CLEARING canvas");
26840
+ console.log("[CroppedHlsVideoPlayer] Video paused, stopping canvas rendering and CLEARING canvas");
27177
26841
  stopCanvasRendering();
27178
26842
  setIsProcessing(false);
27179
26843
  if (canvasRef.current) {
@@ -27184,10 +26848,10 @@ var CroppedVideoPlayer = forwardRef(({
27184
26848
  ctx.fillRect(0, 0, canvasRef.current.width, canvasRef.current.height);
27185
26849
  }
27186
26850
  }
27187
- videoProps.onPause?.(player);
27188
- }, [stopCanvasRendering, videoProps]);
26851
+ onPauseProp?.(player);
26852
+ }, [stopCanvasRendering, onPauseProp]);
27189
26853
  const handleVideoEnded = useCallback((player) => {
27190
- console.log("[CroppedVideoPlayer] Video ended, CLEARING canvas");
26854
+ console.log("[CroppedHlsVideoPlayer] Video ended, CLEARING canvas");
27191
26855
  stopCanvasRendering();
27192
26856
  setIsProcessing(false);
27193
26857
  if (canvasRef.current) {
@@ -27198,145 +26862,677 @@ var CroppedVideoPlayer = forwardRef(({
27198
26862
  ctx.fillRect(0, 0, canvasRef.current.width, canvasRef.current.height);
27199
26863
  }
27200
26864
  }
27201
- videoProps.onEnded?.(player);
27202
- }, [stopCanvasRendering, videoProps]);
26865
+ onEndedProp?.(player);
26866
+ }, [stopCanvasRendering, onEndedProp]);
27203
26867
  const handleSeeking = useCallback((player) => {
27204
- console.log("[CroppedVideoPlayer] Video seeking");
27205
- if (crop && !player.paused()) {
26868
+ console.log("[CroppedHlsVideoPlayer] Video seeking");
26869
+ if (crop && !videoElementRef.current?.paused) {
27206
26870
  renderFrameToCanvas();
27207
26871
  }
27208
- videoProps.onSeeking?.(player);
27209
- }, [crop, renderFrameToCanvas, videoProps]);
26872
+ onSeekingProp?.(player);
26873
+ }, [crop, renderFrameToCanvas, onSeekingProp]);
27210
26874
  const handleSeeked = useCallback((player) => {
27211
- console.log("[CroppedVideoPlayer] Video seeked");
27212
- if (crop && !player.paused()) {
26875
+ console.log("[CroppedHlsVideoPlayer] Video seeked");
26876
+ if (crop && !videoElementRef.current?.paused) {
27213
26877
  renderFrameToCanvas();
27214
26878
  }
27215
- videoProps.onSeeked?.(player);
27216
- }, [crop, renderFrameToCanvas, videoProps]);
27217
- const handleLoadedMetadata = useCallback((player) => {
27218
- console.log("[CroppedVideoPlayer] Video metadata loaded");
27219
- calculateCanvasDimensions();
27220
- videoProps.onLoadedMetadata?.(player);
27221
- }, [calculateCanvasDimensions, videoProps]);
26879
+ onSeekedProp?.(player);
26880
+ }, [crop, renderFrameToCanvas, onSeekedProp]);
26881
+ const handleLoadedMetadata = useCallback((player) => {
26882
+ console.log("[CroppedHlsVideoPlayer] Video metadata loaded");
26883
+ calculateCanvasDimensions();
26884
+ onLoadedMetadataProp?.(player);
26885
+ }, [calculateCanvasDimensions, onLoadedMetadataProp]);
26886
+ useEffect(() => {
26887
+ calculateCanvasDimensions();
26888
+ const handleResize = () => {
26889
+ calculateCanvasDimensions();
26890
+ };
26891
+ window.addEventListener("resize", handleResize);
26892
+ return () => {
26893
+ window.removeEventListener("resize", handleResize);
26894
+ };
26895
+ }, [calculateCanvasDimensions]);
26896
+ useLayoutEffect(() => {
26897
+ if (canvasRef.current && crop) {
26898
+ const canvas = canvasRef.current;
26899
+ const ctx = canvas.getContext("2d");
26900
+ if (ctx) {
26901
+ console.log("[CroppedHlsVideoPlayer] Source changing - CLEARING CANVAS IMMEDIATELY");
26902
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
26903
+ ctx.fillStyle = "black";
26904
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
26905
+ }
26906
+ }
26907
+ }, [videoSrc, crop]);
26908
+ useEffect(() => {
26909
+ return () => {
26910
+ stopCanvasRendering();
26911
+ };
26912
+ }, [stopCanvasRendering]);
26913
+ if (!crop) {
26914
+ return /* @__PURE__ */ jsx(HlsVideoPlayer, { ref, ...videoProps, onClick });
26915
+ }
26916
+ const handleClickWithIndicator = () => {
26917
+ if (!onClick || !hiddenVideoRef.current?.video) return;
26918
+ const video = hiddenVideoRef.current.video;
26919
+ const willBePlaying = video.paused;
26920
+ setIndicatorIsPlaying(willBePlaying);
26921
+ setShowIndicator(false);
26922
+ setTimeout(() => {
26923
+ indicatorKeyRef.current += 1;
26924
+ setShowIndicator(true);
26925
+ }, 0);
26926
+ onClick();
26927
+ };
26928
+ return /* @__PURE__ */ jsxs(
26929
+ "div",
26930
+ {
26931
+ ref: videoContainerRef,
26932
+ className: `relative w-full h-full flex items-center justify-center bg-black ${onClick ? "cursor-pointer" : ""} ${inheritedClassName}`,
26933
+ onClick: handleClickWithIndicator,
26934
+ children: [
26935
+ /* @__PURE__ */ jsx("div", { className: "hidden", children: /* @__PURE__ */ jsx(
26936
+ HlsVideoPlayer,
26937
+ {
26938
+ ref: hiddenVideoRef,
26939
+ ...videoProps,
26940
+ onReady: handleVideoReady,
26941
+ onPlay: handleVideoPlay,
26942
+ onPause: handleVideoPause,
26943
+ onEnded: handleVideoEnded,
26944
+ onSeeking: handleSeeking,
26945
+ onSeeked: handleSeeked,
26946
+ onLoadedMetadata: handleLoadedMetadata,
26947
+ onLoadedData: videoProps.onLoadedData,
26948
+ onPlaying: videoProps.onPlaying,
26949
+ onLoadingChange: videoProps.onLoadingChange
26950
+ }
26951
+ ) }),
26952
+ /* @__PURE__ */ jsx(
26953
+ "canvas",
26954
+ {
26955
+ ref: canvasRef,
26956
+ width: canvasDimensions.width,
26957
+ height: canvasDimensions.height,
26958
+ className: "max-w-full max-h-full",
26959
+ style: {
26960
+ display: isVideoReady ? "block" : "none",
26961
+ width: `${canvasDimensions.width}px`,
26962
+ height: `${canvasDimensions.height}px`
26963
+ }
26964
+ }
26965
+ ),
26966
+ !isVideoReady && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
26967
+ debug && isVideoReady && /* @__PURE__ */ jsxs("div", { className: "absolute top-2 left-2 bg-black/80 text-white text-xs p-2 rounded font-mono", children: [
26968
+ /* @__PURE__ */ jsxs("div", { children: [
26969
+ "Crop: ",
26970
+ crop.x,
26971
+ ",",
26972
+ crop.y,
26973
+ " ",
26974
+ crop.width,
26975
+ "x",
26976
+ crop.height,
26977
+ "%"
26978
+ ] }),
26979
+ /* @__PURE__ */ jsxs("div", { children: [
26980
+ "Canvas: ",
26981
+ canvasDimensions.width,
26982
+ "x",
26983
+ canvasDimensions.height,
26984
+ "px"
26985
+ ] }),
26986
+ /* @__PURE__ */ jsxs("div", { children: [
26987
+ "Processing: ",
26988
+ isProcessing ? "Yes" : "No"
26989
+ ] })
26990
+ ] }),
26991
+ onClick && /* @__PURE__ */ jsx(
26992
+ PlayPauseIndicator,
26993
+ {
26994
+ show: showIndicator,
26995
+ isPlaying: indicatorIsPlaying
26996
+ },
26997
+ indicatorKeyRef.current
26998
+ )
26999
+ ]
27000
+ }
27001
+ );
27002
+ });
27003
+ CroppedHlsVideoPlayer.displayName = "CroppedHlsVideoPlayer";
27004
+ var CroppedVideoPlayer = CroppedHlsVideoPlayer;
27005
+ var getSupabaseClient2 = () => {
27006
+ const url = process.env.NEXT_PUBLIC_SUPABASE_URL;
27007
+ const key = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
27008
+ if (!url || !key) {
27009
+ throw new Error("Supabase configuration missing");
27010
+ }
27011
+ return createClient(url, key);
27012
+ };
27013
+ var getAuthToken3 = async () => {
27014
+ try {
27015
+ const supabase = getSupabaseClient2();
27016
+ const { data: { session } } = await supabase.auth.getSession();
27017
+ return session?.access_token || null;
27018
+ } catch (error) {
27019
+ console.error("[useWorkspaceCrop] Error getting auth token:", error);
27020
+ return null;
27021
+ }
27022
+ };
27023
+ function useWorkspaceCrop(workspaceId) {
27024
+ const [crop, setCrop] = useState(null);
27025
+ const [isLoading, setIsLoading] = useState(true);
27026
+ const [error, setError] = useState(null);
27027
+ useEffect(() => {
27028
+ if (!workspaceId) {
27029
+ setIsLoading(false);
27030
+ return;
27031
+ }
27032
+ const fetchCrop = async () => {
27033
+ setIsLoading(true);
27034
+ setError(null);
27035
+ try {
27036
+ const token = await getAuthToken3();
27037
+ if (!token) {
27038
+ throw new Error("Authentication required");
27039
+ }
27040
+ const response = await fetch("/api/clips/supabase", {
27041
+ method: "POST",
27042
+ headers: {
27043
+ "Content-Type": "application/json",
27044
+ "Authorization": `Bearer ${token}`
27045
+ },
27046
+ body: JSON.stringify({
27047
+ action: "crop",
27048
+ workspaceId
27049
+ })
27050
+ });
27051
+ if (!response.ok) {
27052
+ throw new Error(`Failed to fetch crop: ${response.statusText}`);
27053
+ }
27054
+ const data = await response.json();
27055
+ console.log(`[useWorkspaceCrop] Fetched crop for workspace ${workspaceId}:`, data.crop);
27056
+ setCrop(data.crop);
27057
+ } catch (err) {
27058
+ console.error("[useWorkspaceCrop] Error fetching crop:", err);
27059
+ setError(err instanceof Error ? err.message : "Failed to fetch crop configuration");
27060
+ setCrop(null);
27061
+ } finally {
27062
+ setIsLoading(false);
27063
+ }
27064
+ };
27065
+ fetchCrop();
27066
+ }, [workspaceId]);
27067
+ return { crop, isLoading, error };
27068
+ }
27069
+ function Skeleton({ className, ...props }) {
27070
+ return /* @__PURE__ */ jsx("div", { className: cn("animate-pulse rounded-md bg-muted", className), ...props });
27071
+ }
27072
+ var Select = SelectPrimitive.Root;
27073
+ var SelectGroup = SelectPrimitive.Group;
27074
+ var SelectValue = SelectPrimitive.Value;
27075
+ var SelectTrigger = React23.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
27076
+ SelectPrimitive.Trigger,
27077
+ {
27078
+ ref,
27079
+ className: cn(
27080
+ "flex h-11 sm:h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background data-[placeholder]:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1 touch-manipulation",
27081
+ className
27082
+ ),
27083
+ ...props,
27084
+ children: [
27085
+ children,
27086
+ /* @__PURE__ */ jsx(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4 opacity-50" }) })
27087
+ ]
27088
+ }
27089
+ ));
27090
+ SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
27091
+ var SelectScrollUpButton = React23.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
27092
+ SelectPrimitive.ScrollUpButton,
27093
+ {
27094
+ ref,
27095
+ className: cn("flex cursor-default items-center justify-center py-1", className),
27096
+ ...props,
27097
+ children: /* @__PURE__ */ jsx(ChevronUp, { className: "h-4 w-4" })
27098
+ }
27099
+ ));
27100
+ SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
27101
+ var SelectScrollDownButton = React23.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
27102
+ SelectPrimitive.ScrollDownButton,
27103
+ {
27104
+ ref,
27105
+ className: cn("flex cursor-default items-center justify-center py-1", className),
27106
+ ...props,
27107
+ children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4" })
27108
+ }
27109
+ ));
27110
+ SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
27111
+ var SelectContent = React23.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
27112
+ SelectPrimitive.Content,
27113
+ {
27114
+ ref,
27115
+ className: cn(
27116
+ "relative z-50 max-h-[--radix-select-content-available-height] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-select-content-transform-origin]",
27117
+ position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
27118
+ className
27119
+ ),
27120
+ position,
27121
+ ...props,
27122
+ children: [
27123
+ /* @__PURE__ */ jsx(SelectScrollUpButton, {}),
27124
+ /* @__PURE__ */ jsx(
27125
+ SelectPrimitive.Viewport,
27126
+ {
27127
+ className: cn(
27128
+ "p-1",
27129
+ position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
27130
+ ),
27131
+ children
27132
+ }
27133
+ ),
27134
+ /* @__PURE__ */ jsx(SelectScrollDownButton, {})
27135
+ ]
27136
+ }
27137
+ ) }));
27138
+ SelectContent.displayName = SelectPrimitive.Content.displayName;
27139
+ var SelectLabel = React23.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
27140
+ SelectPrimitive.Label,
27141
+ {
27142
+ ref,
27143
+ className: cn("px-2 py-1.5 text-sm font-semibold", className),
27144
+ ...props
27145
+ }
27146
+ ));
27147
+ SelectLabel.displayName = SelectPrimitive.Label.displayName;
27148
+ var SelectItem = React23.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
27149
+ SelectPrimitive.Item,
27150
+ {
27151
+ ref,
27152
+ className: cn(
27153
+ "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
27154
+ className
27155
+ ),
27156
+ ...props,
27157
+ children: [
27158
+ /* @__PURE__ */ jsx("span", { className: "absolute right-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx(Check, { className: "h-4 w-4" }) }) }),
27159
+ /* @__PURE__ */ jsx(SelectPrimitive.ItemText, { children })
27160
+ ]
27161
+ }
27162
+ ));
27163
+ SelectItem.displayName = SelectPrimitive.Item.displayName;
27164
+ var SelectSeparator = React23.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
27165
+ SelectPrimitive.Separator,
27166
+ {
27167
+ ref,
27168
+ className: cn("-mx-1 my-1 h-px bg-muted", className),
27169
+ ...props
27170
+ }
27171
+ ));
27172
+ SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
27173
+ var LoadingOverlay = ({
27174
+ isVisible,
27175
+ message = "Loading...",
27176
+ className
27177
+ }) => {
27178
+ if (!isVisible) return null;
27179
+ return /* @__PURE__ */ jsx(
27180
+ motion.div,
27181
+ {
27182
+ initial: { opacity: 0 },
27183
+ animate: { opacity: 1 },
27184
+ exit: { opacity: 0 },
27185
+ transition: { duration: 0.2 },
27186
+ className: `fixed inset-0 z-[100] flex items-center justify-center bg-black/30 backdrop-blur-sm ${className || ""}`,
27187
+ "aria-modal": "true",
27188
+ role: "dialog",
27189
+ children: /* @__PURE__ */ jsx("div", { className: "flex flex-col items-center space-y-3 rounded-lg bg-white p-8 shadow-xl", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message }) })
27190
+ }
27191
+ );
27192
+ };
27193
+ var LoadingOverlay_default = LoadingOverlay;
27194
+ var TimeDisplay = ({ className, variant = "default" }) => {
27195
+ const { dateTimeConfig } = useDashboardConfig();
27196
+ const [time2, setTime] = useState("");
27197
+ const dbTimezone = useAppTimezone();
27198
+ const timezoneToDisplay = dbTimezone || dateTimeConfig?.defaultTimezone || "UTC";
27199
+ const localeToUse = dateTimeConfig?.defaultLocale || "en-US";
27200
+ const timeSuffix = "";
27201
+ useEffect(() => {
27202
+ const updateTime = () => {
27203
+ const now2 = /* @__PURE__ */ new Date();
27204
+ const effectiveFormatOptions = {
27205
+ hour: "2-digit",
27206
+ minute: "2-digit",
27207
+ second: "2-digit",
27208
+ hour12: true,
27209
+ timeZone: timezoneToDisplay,
27210
+ ...dateTimeConfig?.timeFormatOptions || {}
27211
+ // Allow override from config
27212
+ };
27213
+ try {
27214
+ setTime(new Intl.DateTimeFormat(localeToUse, effectiveFormatOptions).format(now2));
27215
+ } catch (e) {
27216
+ console.error("Error formatting time:", e);
27217
+ setTime("Error");
27218
+ }
27219
+ };
27220
+ updateTime();
27221
+ const interval = setInterval(updateTime, 1e3);
27222
+ return () => clearInterval(interval);
27223
+ }, [timezoneToDisplay, dateTimeConfig?.timeFormatOptions, localeToUse]);
27224
+ if (!time2) return null;
27225
+ if (variant === "minimal") {
27226
+ return /* @__PURE__ */ jsxs("span", { className: className || "", children: [
27227
+ time2,
27228
+ " ",
27229
+ timeSuffix
27230
+ ] });
27231
+ }
27232
+ return /* @__PURE__ */ jsxs(
27233
+ motion.div,
27234
+ {
27235
+ initial: { opacity: 0, y: -5 },
27236
+ animate: { opacity: 1, y: 0 },
27237
+ transition: { duration: 0.3 },
27238
+ className: `flex items-center space-x-1.5 bg-white/60 backdrop-blur-sm px-2 py-0.5 rounded-md shadow-xs ${className || ""}`,
27239
+ children: [
27240
+ /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-3 w-3 text-[var(--primary-DEFAULT)]", viewBox: "0 0 20 20", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z", clipRule: "evenodd" }) }),
27241
+ /* @__PURE__ */ jsxs("span", { className: "text-xs sm:text-[11px] font-medium text-gray-800 tabular-nums tracking-tight", children: [
27242
+ time2,
27243
+ " ",
27244
+ timeSuffix
27245
+ ] })
27246
+ ]
27247
+ }
27248
+ );
27249
+ };
27250
+ var TimeDisplay_default = TimeDisplay;
27251
+ var DateDisplay = ({ className, variant = "default" }) => {
27252
+ const { dateTimeConfig } = useDashboardConfig();
27253
+ const [date, setDate] = useState("");
27254
+ const timezoneToDisplay = dateTimeConfig?.defaultTimezone || "UTC";
27255
+ const localeToUse = dateTimeConfig?.defaultLocale || "en-US";
27222
27256
  useEffect(() => {
27223
- calculateCanvasDimensions();
27224
- const handleResize = () => {
27225
- calculateCanvasDimensions();
27257
+ const getCurrentFormattedDate = () => {
27258
+ const now2 = /* @__PURE__ */ new Date();
27259
+ const effectiveFormatOptions = {
27260
+ weekday: "short",
27261
+ day: "numeric",
27262
+ month: "short",
27263
+ timeZone: timezoneToDisplay,
27264
+ ...dateTimeConfig?.dateFormatOptions || {}
27265
+ // Allow override from config
27266
+ };
27267
+ try {
27268
+ return new Intl.DateTimeFormat(localeToUse, effectiveFormatOptions).format(now2);
27269
+ } catch (e) {
27270
+ console.error("Error formatting date:", e);
27271
+ return "Error";
27272
+ }
27226
27273
  };
27227
- window.addEventListener("resize", handleResize);
27228
- return () => {
27229
- window.removeEventListener("resize", handleResize);
27274
+ const updateDate = () => {
27275
+ setDate(getCurrentFormattedDate());
27230
27276
  };
27231
- }, [calculateCanvasDimensions]);
27232
- useLayoutEffect(() => {
27233
- if (canvasRef.current && crop) {
27234
- const canvas = canvasRef.current;
27235
- const ctx = canvas.getContext("2d");
27236
- if (ctx) {
27237
- console.log("[CroppedVideoPlayer] Source changing - CLEARING CANVAS IMMEDIATELY");
27238
- ctx.clearRect(0, 0, canvas.width, canvas.height);
27239
- ctx.fillStyle = "black";
27240
- ctx.fillRect(0, 0, canvas.width, canvas.height);
27277
+ updateDate();
27278
+ const interval = setInterval(() => {
27279
+ const currentDateStr = getCurrentFormattedDate();
27280
+ if (currentDateStr !== date) {
27281
+ updateDate();
27241
27282
  }
27242
- }
27243
- }, [videoProps.src, crop]);
27244
- useEffect(() => {
27245
- return () => {
27246
- stopCanvasRendering();
27247
- };
27248
- }, [stopCanvasRendering]);
27249
- if (!crop) {
27250
- return /* @__PURE__ */ jsx(VideoPlayer, { ref, ...videoProps, onClick });
27283
+ }, 60 * 1e3);
27284
+ return () => clearInterval(interval);
27285
+ }, [date, timezoneToDisplay, dateTimeConfig?.dateFormatOptions, localeToUse]);
27286
+ if (!date) return null;
27287
+ if (variant === "minimal") {
27288
+ return /* @__PURE__ */ jsx("span", { className: className || "", children: date });
27251
27289
  }
27252
- const handleClickWithIndicator = () => {
27253
- if (!onClick || !hiddenVideoRef.current?.player) return;
27254
- const player = hiddenVideoRef.current.player;
27255
- const willBePlaying = player.paused();
27256
- setIndicatorIsPlaying(willBePlaying);
27257
- setShowIndicator(false);
27258
- setTimeout(() => {
27259
- indicatorKeyRef.current += 1;
27260
- setShowIndicator(true);
27261
- }, 0);
27262
- onClick();
27263
- };
27264
27290
  return /* @__PURE__ */ jsxs(
27265
- "div",
27291
+ motion.div,
27266
27292
  {
27267
- ref: videoContainerRef,
27268
- className: `relative w-full h-full flex items-center justify-center bg-black ${onClick ? "cursor-pointer" : ""} ${videoProps.className || ""}`,
27269
- onClick: handleClickWithIndicator,
27293
+ initial: { opacity: 0, y: -5 },
27294
+ animate: { opacity: 1, y: 0 },
27295
+ transition: { duration: 0.3 },
27296
+ className: `flex items-center space-x-1.5 bg-white/60 backdrop-blur-sm px-2 py-0.5 rounded-md shadow-xs ${className || ""}`,
27270
27297
  children: [
27271
- /* @__PURE__ */ jsx("div", { className: "hidden", children: /* @__PURE__ */ jsx(
27272
- VideoPlayer,
27273
- {
27274
- ref: hiddenVideoRef,
27275
- ...videoProps,
27276
- onReady: handleVideoReady,
27277
- onPlay: handleVideoPlay,
27278
- onPause: handleVideoPause,
27279
- onEnded: handleVideoEnded,
27280
- onSeeking: handleSeeking,
27281
- onSeeked: handleSeeked,
27282
- onLoadedMetadata: handleLoadedMetadata,
27283
- onLoadedData: videoProps.onLoadedData,
27284
- onPlaying: videoProps.onPlaying,
27285
- onLoadingChange: videoProps.onLoadingChange
27286
- }
27287
- ) }),
27288
- /* @__PURE__ */ jsx(
27289
- "canvas",
27290
- {
27291
- ref: canvasRef,
27292
- width: canvasDimensions.width,
27293
- height: canvasDimensions.height,
27294
- className: "max-w-full max-h-full",
27295
- style: {
27296
- display: isVideoReady ? "block" : "none",
27297
- width: `${canvasDimensions.width}px`,
27298
- height: `${canvasDimensions.height}px`
27299
- }
27300
- }
27301
- ),
27302
- !isVideoReady && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
27303
- debug && isVideoReady && /* @__PURE__ */ jsxs("div", { className: "absolute top-2 left-2 bg-black/80 text-white text-xs p-2 rounded font-mono", children: [
27304
- /* @__PURE__ */ jsxs("div", { children: [
27305
- "Crop: ",
27306
- crop.x,
27307
- ",",
27308
- crop.y,
27309
- " ",
27310
- crop.width,
27311
- "x",
27312
- crop.height,
27313
- "%"
27314
- ] }),
27315
- /* @__PURE__ */ jsxs("div", { children: [
27316
- "Canvas: ",
27317
- canvasDimensions.width,
27318
- "x",
27319
- canvasDimensions.height,
27320
- "px"
27321
- ] }),
27322
- /* @__PURE__ */ jsxs("div", { children: [
27323
- "Processing: ",
27324
- isProcessing ? "Yes" : "No"
27325
- ] })
27326
- ] }),
27327
- onClick && /* @__PURE__ */ jsx(
27328
- PlayPauseIndicator,
27329
- {
27330
- show: showIndicator,
27331
- isPlaying: indicatorIsPlaying
27332
- },
27333
- indicatorKeyRef.current
27334
- )
27298
+ /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-3 w-3 text-blue-600", viewBox: "0 0 20 20", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M6 2a1 1 0 00-1 1v1H4a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-1V3a1 1 0 10-2 0v1H7V3a1 1 0 00-1-1zm0 5a1 1 0 000 2h8a1 1 0 100-2H6z", clipRule: "evenodd" }) }),
27299
+ /* @__PURE__ */ jsx("span", { className: "text-[11px] font-medium text-gray-800 tracking-tight", children: date })
27335
27300
  ]
27336
27301
  }
27337
27302
  );
27338
- });
27339
- CroppedVideoPlayer.displayName = "CroppedVideoPlayer";
27303
+ };
27304
+ var DateDisplay_default = DateDisplay;
27305
+ var Card3 = Card2;
27306
+ var CardHeader3 = CardHeader2;
27307
+ var CardTitle3 = CardTitle2;
27308
+ var CardContent3 = CardContent2;
27309
+ var MetricCard2 = ({ title, value, unit = "", trend = null }) => {
27310
+ const getTrendColor = (trendValue) => {
27311
+ if (trendValue === null || trendValue === void 0) return "";
27312
+ return trendValue > 0 ? "text-green-500" : trendValue < 0 ? "text-red-500" : "text-gray-500";
27313
+ };
27314
+ const getTrendSymbol = (trendValue) => {
27315
+ if (trendValue === null || trendValue === void 0) return "";
27316
+ return trendValue > 0 ? "\u2191" : trendValue < 0 ? "\u2193" : "";
27317
+ };
27318
+ return /* @__PURE__ */ jsxs(Card3, { className: "bg-white", children: [
27319
+ /* @__PURE__ */ jsx(CardHeader3, { className: "pb-2", children: /* @__PURE__ */ jsx(CardTitle3, { className: "text-sm font-medium text-gray-500", children: title }) }),
27320
+ /* @__PURE__ */ jsx(CardContent3, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between", children: [
27321
+ /* @__PURE__ */ jsxs("div", { className: "text-2xl font-semibold", children: [
27322
+ value,
27323
+ unit && /* @__PURE__ */ jsx("span", { className: "ml-1 text-sm text-gray-500", children: unit })
27324
+ ] }),
27325
+ trend !== null && trend !== void 0 && // Check trend for null/undefined before accessing
27326
+ /* @__PURE__ */ jsx("div", { className: `flex items-center ${getTrendColor(trend)}`, children: /* @__PURE__ */ jsxs("span", { className: "text-sm", children: [
27327
+ getTrendSymbol(trend),
27328
+ " ",
27329
+ Math.abs(trend),
27330
+ "%"
27331
+ ] }) })
27332
+ ] }) })
27333
+ ] });
27334
+ };
27335
+ var MetricCard_default = MetricCard2;
27336
+ var TimePickerDropdown = ({
27337
+ value,
27338
+ onChange,
27339
+ placeholder = "Select time",
27340
+ className = "",
27341
+ disabled = false
27342
+ }) => {
27343
+ const [isOpen, setIsOpen] = useState(false);
27344
+ const [searchTerm, setSearchTerm] = useState("");
27345
+ const dropdownRef = useRef(null);
27346
+ const inputRef = useRef(null);
27347
+ const generateTimeSlots = () => {
27348
+ const slots = [];
27349
+ for (let hour = 0; hour < 24; hour++) {
27350
+ for (let minute = 0; minute < 60; minute += 15) {
27351
+ const time24 = `${hour.toString().padStart(2, "0")}:${minute.toString().padStart(2, "0")}`;
27352
+ const hour12 = hour === 0 ? 12 : hour > 12 ? hour - 12 : hour;
27353
+ const ampm = hour < 12 ? "AM" : "PM";
27354
+ const time12 = `${hour12}:${minute.toString().padStart(2, "0")} ${ampm}`;
27355
+ slots.push({ value: time24, label: time12 });
27356
+ }
27357
+ }
27358
+ return slots;
27359
+ };
27360
+ const timeSlots = generateTimeSlots();
27361
+ const filteredSlots = timeSlots.filter(
27362
+ (slot) => slot.label.toLowerCase().includes(searchTerm.toLowerCase())
27363
+ );
27364
+ const getDisplayValue = (value2) => {
27365
+ if (!value2) return "";
27366
+ const normalizedValue = value2.substring(0, 5);
27367
+ const slot = timeSlots.find((s) => s.value === normalizedValue);
27368
+ return slot ? slot.label : value2;
27369
+ };
27370
+ useEffect(() => {
27371
+ const handleClickOutside = (event) => {
27372
+ if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
27373
+ setIsOpen(false);
27374
+ setSearchTerm("");
27375
+ }
27376
+ };
27377
+ document.addEventListener("mousedown", handleClickOutside);
27378
+ return () => document.removeEventListener("mousedown", handleClickOutside);
27379
+ }, []);
27380
+ const handleKeyDown = (e) => {
27381
+ if (e.key === "Escape") {
27382
+ setIsOpen(false);
27383
+ setSearchTerm("");
27384
+ } else if (e.key === "Enter") {
27385
+ e.preventDefault();
27386
+ if (filteredSlots.length > 0) {
27387
+ onChange(filteredSlots[0].value);
27388
+ setIsOpen(false);
27389
+ setSearchTerm("");
27390
+ }
27391
+ }
27392
+ };
27393
+ const handleSelect = (timeValue) => {
27394
+ onChange(timeValue);
27395
+ setIsOpen(false);
27396
+ setSearchTerm("");
27397
+ };
27398
+ const handleToggle = () => {
27399
+ if (disabled) return;
27400
+ setIsOpen(!isOpen);
27401
+ if (!isOpen) {
27402
+ setTimeout(() => inputRef.current?.focus(), 100);
27403
+ }
27404
+ };
27405
+ return /* @__PURE__ */ jsxs("div", { className: `relative ${className}`, ref: dropdownRef, children: [
27406
+ /* @__PURE__ */ jsx(
27407
+ "button",
27408
+ {
27409
+ type: "button",
27410
+ onClick: handleToggle,
27411
+ disabled,
27412
+ className: `
27413
+ w-full px-3 py-2 text-left bg-white border border-gray-300 rounded-md shadow-sm
27414
+ focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500
27415
+ hover:border-gray-400 transition-colors duration-200
27416
+ ${disabled ? "bg-gray-50 cursor-not-allowed" : "cursor-pointer"}
27417
+ ${isOpen ? "ring-2 ring-blue-500 border-blue-500" : ""}
27418
+ `,
27419
+ children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
27420
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
27421
+ /* @__PURE__ */ jsx(Clock, { className: "h-4 w-4 text-gray-400" }),
27422
+ /* @__PURE__ */ jsx("span", { className: `text-sm ${value ? "text-gray-900" : "text-gray-500"}`, children: value ? getDisplayValue(value) : placeholder })
27423
+ ] }),
27424
+ /* @__PURE__ */ jsx(
27425
+ ChevronDown,
27426
+ {
27427
+ className: `h-4 w-4 text-gray-400 transition-transform duration-200 ${isOpen ? "rotate-180" : ""}`
27428
+ }
27429
+ )
27430
+ ] })
27431
+ }
27432
+ ),
27433
+ isOpen && /* @__PURE__ */ jsxs("div", { className: "absolute z-50 w-full mt-1 bg-white border border-gray-300 rounded-md shadow-lg max-h-60 overflow-hidden", children: [
27434
+ /* @__PURE__ */ jsx("div", { className: "p-2 border-b border-gray-200", children: /* @__PURE__ */ jsx(
27435
+ "input",
27436
+ {
27437
+ ref: inputRef,
27438
+ type: "text",
27439
+ placeholder: "Search time...",
27440
+ value: searchTerm,
27441
+ onChange: (e) => setSearchTerm(e.target.value),
27442
+ onKeyDown: handleKeyDown,
27443
+ className: "w-full px-3 py-2 text-sm border border-gray-200 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
27444
+ }
27445
+ ) }),
27446
+ /* @__PURE__ */ jsx("div", { className: "overflow-y-auto max-h-48", children: filteredSlots.length === 0 ? /* @__PURE__ */ jsx("div", { className: "px-3 py-2 text-sm text-gray-500 text-center", children: "No times found" }) : filteredSlots.map((slot) => /* @__PURE__ */ jsx(
27447
+ "button",
27448
+ {
27449
+ type: "button",
27450
+ onClick: () => handleSelect(slot.value),
27451
+ className: `
27452
+ w-full px-3 py-2 text-left text-sm hover:bg-blue-50 hover:text-blue-600
27453
+ transition-colors duration-150 border-b border-gray-100 last:border-b-0
27454
+ ${slot.value === value ? "bg-blue-50 text-blue-600 font-medium" : "text-gray-700"}
27455
+ `,
27456
+ children: slot.label
27457
+ },
27458
+ slot.value
27459
+ )) })
27460
+ ] })
27461
+ ] });
27462
+ };
27463
+ var SilentErrorBoundary = class extends React23__default.Component {
27464
+ constructor(props) {
27465
+ super(props);
27466
+ this.handleClearAndReload = () => {
27467
+ console.log("[ErrorBoundary] User initiated reset");
27468
+ if (typeof window !== "undefined") {
27469
+ try {
27470
+ localStorage.clear();
27471
+ sessionStorage.clear();
27472
+ console.log("[ErrorBoundary] Cleared all storage");
27473
+ } catch (error) {
27474
+ console.error("[ErrorBoundary] Failed to clear storage:", error);
27475
+ }
27476
+ }
27477
+ window.location.href = "/login";
27478
+ };
27479
+ this.state = {
27480
+ hasError: false,
27481
+ errorCount: 0,
27482
+ lastError: null,
27483
+ errorInfo: null
27484
+ };
27485
+ }
27486
+ static getDerivedStateFromError(error) {
27487
+ return { hasError: true };
27488
+ }
27489
+ componentDidCatch(error, errorInfo) {
27490
+ console.error("[ErrorBoundary] Caught render error:", {
27491
+ error: error.message,
27492
+ stack: error.stack,
27493
+ componentStack: errorInfo.componentStack,
27494
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
27495
+ });
27496
+ this.setState((prev) => ({
27497
+ errorCount: prev.errorCount + 1,
27498
+ lastError: error,
27499
+ errorInfo
27500
+ }));
27501
+ try {
27502
+ if (typeof window !== "undefined" && window.mixpanel) {
27503
+ window.mixpanel.track("React Render Error", {
27504
+ error: error.message,
27505
+ component: errorInfo.componentStack?.split("\n")[1] || "unknown",
27506
+ errorCount: this.state.errorCount + 1
27507
+ });
27508
+ }
27509
+ } catch (analyticsError) {
27510
+ console.warn("[ErrorBoundary] Analytics tracking failed:", analyticsError);
27511
+ }
27512
+ }
27513
+ render() {
27514
+ if (!this.state.hasError) {
27515
+ return this.props.children;
27516
+ }
27517
+ return /* @__PURE__ */ jsx("div", { className: "flex h-screen w-screen items-center justify-center bg-slate-50", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center space-y-6 text-center", children: [
27518
+ /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "lg", message: "Loading Dashboard..." }),
27519
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500", children: "Taking longer than usual..." }),
27520
+ /* @__PURE__ */ jsx("div", { className: "pt-4", children: /* @__PURE__ */ jsx(
27521
+ "a",
27522
+ {
27523
+ href: "#",
27524
+ onClick: (e) => {
27525
+ e.preventDefault();
27526
+ this.handleClearAndReload();
27527
+ },
27528
+ className: "text-xs text-gray-400 hover:text-gray-600 underline transition-colors",
27529
+ children: "Reset and try again"
27530
+ }
27531
+ ) }),
27532
+ process.env.NODE_ENV === "development" && /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-400 italic mt-4", children: "Check console for error details" })
27533
+ ] }) });
27534
+ }
27535
+ };
27340
27536
  var BackButton = ({
27341
27537
  onClick,
27342
27538
  text = "Back",
@@ -27701,70 +27897,6 @@ var NewClipsNotification = ({
27701
27897
  }
27702
27898
  );
27703
27899
  };
27704
- var getSupabaseClient2 = () => {
27705
- const url = process.env.NEXT_PUBLIC_SUPABASE_URL;
27706
- const key = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
27707
- if (!url || !key) {
27708
- throw new Error("Supabase configuration missing");
27709
- }
27710
- return createClient(url, key);
27711
- };
27712
- var getAuthToken3 = async () => {
27713
- try {
27714
- const supabase = getSupabaseClient2();
27715
- const { data: { session } } = await supabase.auth.getSession();
27716
- return session?.access_token || null;
27717
- } catch (error) {
27718
- console.error("[useWorkspaceCrop] Error getting auth token:", error);
27719
- return null;
27720
- }
27721
- };
27722
- function useWorkspaceCrop(workspaceId) {
27723
- const [crop, setCrop] = useState(null);
27724
- const [isLoading, setIsLoading] = useState(true);
27725
- const [error, setError] = useState(null);
27726
- useEffect(() => {
27727
- if (!workspaceId) {
27728
- setIsLoading(false);
27729
- return;
27730
- }
27731
- const fetchCrop = async () => {
27732
- setIsLoading(true);
27733
- setError(null);
27734
- try {
27735
- const token = await getAuthToken3();
27736
- if (!token) {
27737
- throw new Error("Authentication required");
27738
- }
27739
- const response = await fetch("/api/clips/supabase", {
27740
- method: "POST",
27741
- headers: {
27742
- "Content-Type": "application/json",
27743
- "Authorization": `Bearer ${token}`
27744
- },
27745
- body: JSON.stringify({
27746
- action: "crop",
27747
- workspaceId
27748
- })
27749
- });
27750
- if (!response.ok) {
27751
- throw new Error(`Failed to fetch crop: ${response.statusText}`);
27752
- }
27753
- const data = await response.json();
27754
- console.log(`[useWorkspaceCrop] Fetched crop for workspace ${workspaceId}:`, data.crop);
27755
- setCrop(data.crop);
27756
- } catch (err) {
27757
- console.error("[useWorkspaceCrop] Error fetching crop:", err);
27758
- setError(err instanceof Error ? err.message : "Failed to fetch crop configuration");
27759
- setCrop(null);
27760
- } finally {
27761
- setIsLoading(false);
27762
- }
27763
- };
27764
- fetchCrop();
27765
- }, [workspaceId]);
27766
- return { crop, isLoading, error };
27767
- }
27768
27900
  var parseCycleTime = (value) => {
27769
27901
  if (typeof value === "number" && Number.isFinite(value)) {
27770
27902
  return value;
@@ -29020,6 +29152,11 @@ var BottlenecksContent = ({
29020
29152
  return Number.isFinite(numericValue) ? numericValue : null;
29021
29153
  })();
29022
29154
  const videoRef = useRef(null);
29155
+ const videoPlayerOptions = useMemo(() => ({
29156
+ fluid: false,
29157
+ responsive: false,
29158
+ fill: false
29159
+ }), []);
29023
29160
  const [initialFilter, setInitialFilter] = useState("");
29024
29161
  const currentIndexRef = useRef(0);
29025
29162
  const activeFilterRef = useRef(initialFilter);
@@ -29030,6 +29167,7 @@ var BottlenecksContent = ({
29030
29167
  const [duration, setDuration] = useState(0);
29031
29168
  const [currentIndex, setCurrentIndex] = useState(0);
29032
29169
  const [currentClipId, setCurrentClipId] = useState(null);
29170
+ const [playbackSpeed, setPlaybackSpeed] = useState(1);
29033
29171
  const [isTransitioning, setIsTransitioning] = useState(false);
29034
29172
  const [pendingVideo, setPendingVideo] = useState(null);
29035
29173
  const [isVideoBuffering, setIsVideoBuffering] = useState(false);
@@ -29773,7 +29911,10 @@ var BottlenecksContent = ({
29773
29911
  if (error?.isRetrying) {
29774
29912
  setError(null);
29775
29913
  }
29776
- }, [error]);
29914
+ if (videoRef.current?.playbackRate && playbackSpeed !== 1) {
29915
+ videoRef.current.playbackRate(playbackSpeed);
29916
+ }
29917
+ }, [error, playbackSpeed]);
29777
29918
  const handleVideoPlay = useCallback(async (player) => {
29778
29919
  setIsPlaying(true);
29779
29920
  setIsInitialLoading(false);
@@ -29943,7 +30084,13 @@ var BottlenecksContent = ({
29943
30084
  player.pause();
29944
30085
  }
29945
30086
  };
29946
- const toggleFullscreen = useCallback((e) => {
30087
+ const handlePlaybackSpeedChange = useCallback((speed) => {
30088
+ setPlaybackSpeed(speed);
30089
+ if (videoRef.current?.playbackRate) {
30090
+ videoRef.current.playbackRate(speed);
30091
+ }
30092
+ }, []);
30093
+ useCallback((e) => {
29947
30094
  e.stopPropagation();
29948
30095
  setIsFullscreen((prev) => !prev);
29949
30096
  }, []);
@@ -30117,12 +30264,7 @@ var BottlenecksContent = ({
30117
30264
  onLoadedData: handleLoadedData,
30118
30265
  onPlaying: handleVideoPlaying,
30119
30266
  onLoadingChange: handleVideoLoadingChange,
30120
- options: {
30121
- // Ensure full height is always visible - no cropping
30122
- fluid: false,
30123
- responsive: false,
30124
- fill: false
30125
- }
30267
+ options: videoPlayerOptions
30126
30268
  }
30127
30269
  )
30128
30270
  }
@@ -30183,58 +30325,7 @@ var BottlenecksContent = ({
30183
30325
  /* @__PURE__ */ jsx("span", { className: "font-medium mr-2", children: getClipTypeLabel(currentVideo) }),
30184
30326
  /* @__PURE__ */ jsx("span", { className: "opacity-80 hidden sm:inline", children: currentVideo.description })
30185
30327
  ] }) })
30186
- ),
30187
- /* @__PURE__ */ jsx("div", { className: "absolute bottom-0 left-0 right-0 p-3 bg-gradient-to-t from-black/70 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300 z-10", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-white", children: [
30188
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
30189
- /* @__PURE__ */ jsx(
30190
- "button",
30191
- {
30192
- onClick: (e) => {
30193
- e.stopPropagation();
30194
- togglePlayback();
30195
- },
30196
- className: "p-1.5 hover:bg-white/20 rounded-full focus:outline-none focus:ring-2 focus:ring-white/50",
30197
- "aria-label": isPlaying ? "Pause" : "Play",
30198
- children: isPlaying ? /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", viewBox: "0 0 20 20", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M18 10a8 8 0 11-16 0 8 8 0 0116 0zM7 8a1 1 0 00-1 1v2a1 1 0 102 0V9a1 1 0 00-1-1zm5 0a1 1 0 00-1 1v2a1 1 0 102 0V9a1 1 0 00-1-1z", clipRule: "evenodd" }) }) : /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", viewBox: "0 0 20 20", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M10 18a8 8 0 100-16 8 8 0 000 16zM9.555 7.168A1 1 0 008 8.118l-.001 3.764a1 1 0 001.555.832l3.196-1.882a1 1 0 000-1.664l-3.196-1.882z", clipRule: "evenodd" }) })
30199
- }
30200
- ),
30201
- /* @__PURE__ */ jsxs("span", { className: "text-xs font-mono px-2", children: [
30202
- formatTime2(currentTime),
30203
- " / ",
30204
- formatTime2(duration)
30205
- ] })
30206
- ] }),
30207
- /* @__PURE__ */ jsx(
30208
- "input",
30209
- {
30210
- type: "range",
30211
- min: "0",
30212
- max: duration || 0,
30213
- value: currentTime,
30214
- onChange: (e) => {
30215
- if (videoRef.current) {
30216
- videoRef.current.currentTime(Number(e.target.value));
30217
- }
30218
- },
30219
- className: "flex-grow mx-3 h-2.5 bg-white/30 rounded-full appearance-none cursor-pointer focus:outline-none focus:ring-2 focus:ring-white/50 touch-manipulation",
30220
- style: {
30221
- WebkitAppearance: "none",
30222
- appearance: "none"
30223
- },
30224
- "aria-label": "Seek slider"
30225
- }
30226
- ),
30227
- /* @__PURE__ */ jsx(
30228
- "button",
30229
- {
30230
- onClick: toggleFullscreen,
30231
- className: "p-1.5 hover:bg-white/20 rounded-full focus:outline-none focus:ring-2 focus:ring-white/50",
30232
- "aria-label": "Fullscreen",
30233
- title: "Expand to fullscreen",
30234
- children: /* @__PURE__ */ jsx(Maximize2, { className: "h-5 w-5" })
30235
- }
30236
- )
30237
- ] }) })
30328
+ )
30238
30329
  ] }) }) }) : (
30239
30330
  /* Priority 5: Show "no clips found" only if we have counts and there are truly no clips for workspace */
30240
30331
  hasInitialLoad && Object.keys(mergedCounts).length > 0 && Object.values(mergedCounts).every((count) => count === 0) ? /* @__PURE__ */ jsx("div", { className: `flex items-center justify-center ${triageMode ? "h-full" : "h-[calc(100%-4rem)]"}`, children: /* @__PURE__ */ jsxs("div", { className: "text-center p-8", children: [
@@ -30452,11 +30543,7 @@ var BottlenecksContent = ({
30452
30543
  onLoadedData: handleLoadedData,
30453
30544
  onPlaying: handleVideoPlaying,
30454
30545
  onLoadingChange: handleVideoLoadingChange,
30455
- options: {
30456
- fluid: false,
30457
- responsive: false,
30458
- fill: false
30459
- }
30546
+ options: videoPlayerOptions
30460
30547
  }
30461
30548
  )
30462
30549
  }
@@ -30464,45 +30551,32 @@ var BottlenecksContent = ({
30464
30551
  (isTransitioning || isVideoBuffering && isInitialLoading) && !error && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 z-30 flex items-center justify-center bg-black", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
30465
30552
  !isTransitioning && isVideoBuffering && !isInitialLoading && !error && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 z-30 flex items-center justify-center bg-black/60", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
30466
30553
  /* @__PURE__ */ jsx("div", { className: "absolute bottom-0 left-0 right-0 p-4 bg-gradient-to-t from-black/70 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300 z-10", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-white", children: [
30467
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
30468
- /* @__PURE__ */ jsx(
30469
- "button",
30554
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
30555
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: "Speed:" }),
30556
+ /* @__PURE__ */ jsxs(
30557
+ "select",
30470
30558
  {
30471
- onClick: (e) => {
30472
- e.stopPropagation();
30473
- togglePlayback();
30474
- },
30475
- className: "p-2 hover:bg-white/20 rounded-full focus:outline-none focus:ring-2 focus:ring-white/50",
30476
- "aria-label": isPlaying ? "Pause" : "Play",
30477
- children: isPlaying ? /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-6 w-6", viewBox: "0 0 20 20", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M18 10a8 8 0 11-16 0 8 8 0 0116 0zM7 8a1 1 0 00-1 1v2a1 1 0 102 0V9a1 1 0 00-1-1zm5 0a1 1 0 00-1 1v2a1 1 0 102 0V9a1 1 0 00-1-1z", clipRule: "evenodd" }) }) : /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-6 w-6", viewBox: "0 0 20 20", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M10 18a8 8 0 100-16 8 8 0 000 16zM9.555 7.168A1 1 0 008 8.118l-.001 3.764a1 1 0 001.555.832l3.196-1.882a1 1 0 000-1.664l-3.196-1.882z", clipRule: "evenodd" }) })
30559
+ value: playbackSpeed,
30560
+ onChange: (e) => handlePlaybackSpeedChange(Number(e.target.value)),
30561
+ className: "px-2 py-1 bg-white/20 hover:bg-white/30 rounded text-sm font-medium cursor-pointer focus:outline-none focus:ring-2 focus:ring-white/50 transition-colors",
30562
+ "aria-label": "Playback speed",
30563
+ children: [
30564
+ /* @__PURE__ */ jsx("option", { value: "0.25", children: "0.25x" }),
30565
+ /* @__PURE__ */ jsx("option", { value: "0.5", children: "0.5x" }),
30566
+ /* @__PURE__ */ jsx("option", { value: "0.75", children: "0.75x" }),
30567
+ /* @__PURE__ */ jsx("option", { value: "1", children: "1x" }),
30568
+ /* @__PURE__ */ jsx("option", { value: "1.25", children: "1.25x" }),
30569
+ /* @__PURE__ */ jsx("option", { value: "1.5", children: "1.5x" }),
30570
+ /* @__PURE__ */ jsx("option", { value: "1.75", children: "1.75x" }),
30571
+ /* @__PURE__ */ jsx("option", { value: "2", children: "2x" }),
30572
+ /* @__PURE__ */ jsx("option", { value: "3", children: "3x" }),
30573
+ /* @__PURE__ */ jsx("option", { value: "4", children: "4x" }),
30574
+ /* @__PURE__ */ jsx("option", { value: "5", children: "5x" }),
30575
+ /* @__PURE__ */ jsx("option", { value: "10", children: "10x" })
30576
+ ]
30478
30577
  }
30479
- ),
30480
- /* @__PURE__ */ jsxs("span", { className: "text-sm font-mono px-2", children: [
30481
- formatTime2(currentTime),
30482
- " / ",
30483
- formatTime2(duration)
30484
- ] })
30578
+ )
30485
30579
  ] }),
30486
- /* @__PURE__ */ jsx(
30487
- "input",
30488
- {
30489
- type: "range",
30490
- min: "0",
30491
- max: duration || 0,
30492
- value: currentTime,
30493
- onChange: (e) => {
30494
- if (videoRef.current) {
30495
- videoRef.current.currentTime(Number(e.target.value));
30496
- }
30497
- },
30498
- className: "flex-grow mx-4 h-2.5 bg-white/30 rounded-full appearance-none cursor-pointer focus:outline-none focus:ring-2 focus:ring-white/50 touch-manipulation",
30499
- style: {
30500
- WebkitAppearance: "none",
30501
- appearance: "none"
30502
- },
30503
- "aria-label": "Seek slider"
30504
- }
30505
- ),
30506
30580
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
30507
30581
  /* @__PURE__ */ jsx(
30508
30582
  "button",
@@ -37603,26 +37677,26 @@ var SingleVideoStream = ({
37603
37677
  const hlsStreamUrl = streamUrl || getCameraStreamUrl(workspaceName, baseUrl);
37604
37678
  console.log(`Using camera URL for ${workspaceName}: ${hlsStreamUrl}`);
37605
37679
  const mergedHlsConfig = { ...DEFAULT_HLS_CONFIG, ...hlsConfig };
37606
- if (Hls2.isSupported()) {
37607
- const hls = new Hls2(mergedHlsConfig);
37680
+ if (Hls3.isSupported()) {
37681
+ const hls = new Hls3(mergedHlsConfig);
37608
37682
  hlsRef.current = hls;
37609
- hls.on(Hls2.Events.MEDIA_ATTACHED, () => {
37683
+ hls.on(Hls3.Events.MEDIA_ATTACHED, () => {
37610
37684
  console.log("HLS media attached");
37611
37685
  hls.loadSource(hlsStreamUrl);
37612
37686
  });
37613
- hls.on(Hls2.Events.MANIFEST_PARSED, () => {
37687
+ hls.on(Hls3.Events.MANIFEST_PARSED, () => {
37614
37688
  console.log("HLS manifest parsed");
37615
37689
  attemptPlay(video);
37616
37690
  });
37617
- hls.on(Hls2.Events.ERROR, (_, data) => {
37691
+ hls.on(Hls3.Events.ERROR, (_, data) => {
37618
37692
  if (data.fatal) {
37619
37693
  console.error("Fatal HLS error:", data.type, data.details);
37620
37694
  switch (data.type) {
37621
- case Hls2.ErrorTypes.NETWORK_ERROR:
37695
+ case Hls3.ErrorTypes.NETWORK_ERROR:
37622
37696
  console.error("Fatal network error encountered");
37623
37697
  setError("Network error: Please check your connection");
37624
37698
  break;
37625
- case Hls2.ErrorTypes.MEDIA_ERROR:
37699
+ case Hls3.ErrorTypes.MEDIA_ERROR:
37626
37700
  console.error("Fatal media error encountered, trying to recover");
37627
37701
  hls.recoverMediaError();
37628
37702
  break;
@@ -42383,9 +42457,9 @@ var MetricCards = memo(({ lineInfo }) => {
42383
42457
  animate: "animate",
42384
42458
  className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3 sm:gap-4 mb-2 md:h-[35vh] h-auto",
42385
42459
  children: [
42386
- /* @__PURE__ */ jsxs(motion.div, { variants: itemVariants, className: "bg-white rounded-xl shadow-sm p-3 sm:p-4 overflow-hidden h-[200px] sm:h-[280px] md:h-auto lg:col-span-1 sm:col-span-2 lg:col-span-1", children: [
42387
- /* @__PURE__ */ jsx("h2", { className: "text-sm sm:text-base font-semibold text-gray-700 mb-1 sm:mb-2 text-center", children: "Line Output" }),
42388
- /* @__PURE__ */ jsx("div", { className: "h-[calc(100%-1.75rem)] sm:h-[calc(100%-2.5rem)]", children: /* @__PURE__ */ jsx("div", { className: "w-full h-full flex items-center justify-center", children: /* @__PURE__ */ jsx(
42460
+ /* @__PURE__ */ jsxs(motion.div, { variants: itemVariants, className: "bg-white rounded-xl shadow-sm p-3 sm:p-4 overflow-hidden h-[240px] sm:h-[260px] md:h-auto", children: [
42461
+ /* @__PURE__ */ jsx("h2", { className: "text-sm sm:text-base font-semibold text-gray-700 mb-2 text-center", children: "Line Output" }),
42462
+ /* @__PURE__ */ jsx("div", { className: "h-[calc(100%-2.5rem)]", children: /* @__PURE__ */ jsx("div", { className: "w-full h-full flex items-center justify-center", children: /* @__PURE__ */ jsx(
42389
42463
  OutputProgressChart,
42390
42464
  {
42391
42465
  currentOutput: lineInfo?.metrics.current_output || 0,
@@ -42393,9 +42467,9 @@ var MetricCards = memo(({ lineInfo }) => {
42393
42467
  }
42394
42468
  ) }) })
42395
42469
  ] }),
42396
- /* @__PURE__ */ jsxs(motion.div, { variants: itemVariants, className: "bg-white rounded-xl shadow-sm p-3 sm:p-4 overflow-hidden h-[140px] sm:h-[160px] md:h-auto", children: [
42470
+ /* @__PURE__ */ jsxs(motion.div, { variants: itemVariants, className: "bg-white rounded-xl shadow-sm p-3 sm:p-4 overflow-hidden h-[240px] sm:h-[260px] md:h-auto", children: [
42397
42471
  /* @__PURE__ */ jsx("h2", { className: "text-sm sm:text-base font-semibold text-gray-700 text-center mb-2", children: "Underperforming Workspaces" }),
42398
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center h-[calc(100%-2rem)] sm:h-[calc(100%-2.5rem)]", children: [
42472
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center h-[calc(100%-2.5rem)]", children: [
42399
42473
  /* @__PURE__ */ jsx("span", { className: "text-4xl sm:text-5xl md:text-6xl lg:text-7xl font-bold text-red-600", children: lineInfo?.metrics.underperforming_workspaces }),
42400
42474
  /* @__PURE__ */ jsxs("span", { className: "text-xl sm:text-2xl md:text-2xl lg:text-3xl text-gray-500 ml-1 sm:ml-2", children: [
42401
42475
  "/ ",
@@ -42403,9 +42477,9 @@ var MetricCards = memo(({ lineInfo }) => {
42403
42477
  ] })
42404
42478
  ] })
42405
42479
  ] }),
42406
- /* @__PURE__ */ jsxs(motion.div, { variants: itemVariants, className: "bg-white rounded-xl shadow-sm p-3 sm:p-4 overflow-hidden h-[140px] sm:h-[160px] md:h-auto", children: [
42480
+ /* @__PURE__ */ jsxs(motion.div, { variants: itemVariants, className: "bg-white rounded-xl shadow-sm p-3 sm:p-4 overflow-hidden h-[240px] sm:h-[260px] md:h-auto", children: [
42407
42481
  /* @__PURE__ */ jsx("h2", { className: "text-sm sm:text-base font-semibold text-gray-700 text-center mb-2", children: "Average Efficiency" }),
42408
- /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center h-[calc(100%-2rem)] sm:h-[calc(100%-2.5rem)]", children: /* @__PURE__ */ jsxs("span", { className: `text-4xl sm:text-5xl md:text-6xl lg:text-7xl font-bold ${(lineInfo?.metrics.avg_efficiency || 0) >= 90 ? "text-green-600" : (lineInfo?.metrics.avg_efficiency || 0) >= 70 ? "text-yellow-600" : "text-red-600"}`, children: [
42482
+ /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center h-[calc(100%-2.5rem)]", children: /* @__PURE__ */ jsxs("span", { className: `text-4xl sm:text-5xl md:text-6xl lg:text-7xl font-bold ${(lineInfo?.metrics.avg_efficiency || 0) >= 90 ? "text-green-600" : (lineInfo?.metrics.avg_efficiency || 0) >= 70 ? "text-yellow-600" : "text-red-600"}`, children: [
42409
42483
  lineInfo?.metrics.avg_efficiency.toFixed(1),
42410
42484
  "%"
42411
42485
  ] }) })
@@ -51923,4 +51997,4 @@ function shuffleArray(array) {
51923
51997
  return shuffled;
51924
51998
  }
51925
51999
 
51926
- export { ACTION_NAMES, AIAgentView_default as AIAgentView, AcceptInvite, AcceptInviteView_default as AcceptInviteView, AdvancedFilterDialog, AdvancedFilterPanel, AudioService, AuthCallback, AuthCallbackView_default as AuthCallbackView, AuthProvider, AuthService, AuthenticatedBottleneckClipsView, AuthenticatedFactoryView, AuthenticatedHelpView, AuthenticatedHomeView, AuthenticatedShiftsView, AuthenticatedTargetsView, AuthenticatedTicketsView, AuthenticatedWorkspaceHealthView, AxelNotificationPopup, AxelOrb, BackButton, BackButtonMinimal, BarChart, BaseHistoryCalendar, BottleneckClipsModal, BottleneckClipsView_default as BottleneckClipsView, BottlenecksContent, BreakNotificationPopup, CachePrefetchStatus, Card2 as Card, CardContent2 as CardContent, CardDescription2 as CardDescription, CardFooter2 as CardFooter, CardHeader2 as CardHeader, CardTitle2 as CardTitle, ClipFilterProvider, CompactWorkspaceHealthCard, CongratulationsOverlay, CroppedVideoPlayer, CycleTimeChart, CycleTimeOverTimeChart, DEFAULT_ANALYTICS_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_CONFIG, DEFAULT_DATABASE_CONFIG, DEFAULT_DATE_TIME_CONFIG, DEFAULT_ENDPOINTS_CONFIG, DEFAULT_ENTITY_CONFIG, DEFAULT_MAP_VIEW_CONFIG, DEFAULT_SHIFT_CONFIG, DEFAULT_THEME_CONFIG, DEFAULT_VIDEO_CONFIG, DEFAULT_WORKSPACE_CONFIG, DEFAULT_WORKSPACE_POSITIONS, DashboardHeader, DashboardLayout, DashboardOverridesProvider, DashboardProvider, DateDisplay_default as DateDisplay, DateTimeDisplay, DebugAuth, DebugAuthView_default as DebugAuthView, DetailedHealthStatus, DiagnosisVideoModal, EmptyStateMessage, EncouragementOverlay, FactoryView_default as FactoryView, FileManagerFilters, FilterDialogTrigger, FirstTimeLoginDebug, FirstTimeLoginHandler, GaugeChart, GridComponentsPlaceholder, HamburgerButton, Header, HealthStatusGrid, HealthStatusIndicator, HelpView_default as HelpView, HomeView_default as HomeView, HourlyOutputChart2 as HourlyOutputChart, ISTTimer_default as ISTTimer, InlineEditableText, InteractiveOnboardingTour, InvitationService, KPICard, KPIDetailView_default as KPIDetailView, KPIGrid, KPIHeader, KPISection, KPIsOverviewView_default as KPIsOverviewView, LINE_1_UUID, LINE_2_UUID, LargeOutputProgressChart, LeaderboardDetailView_default as LeaderboardDetailView, Legend6 as Legend, LineChart, LineHistoryCalendar, LineMonthlyHistory, LineMonthlyPdfGenerator, LinePdfExportButton, LinePdfGenerator, LineWhatsAppShareButton, LinesService, LiveTimer, LoadingInline, LoadingOverlay_default as LoadingOverlay, LoadingPage_default as LoadingPage, LoadingSkeleton, LoadingState, LoginPage, LoginView_default as LoginView, Logo, MainLayout, MapGridView, MetricCard_default as MetricCard, MinimalOnboardingPopup, NewClipsNotification, NoWorkspaceData, OnboardingDemo, OnboardingTour, OptifyeAgentClient, OptifyeLogoLoader_default as OptifyeLogoLoader, OutputProgressChart, PageHeader, PieChart4 as PieChart, PlayPauseIndicator, PrefetchConfigurationError, PrefetchError, PrefetchEvents, PrefetchStatus, PrefetchTimeoutError, ProfileView_default as ProfileView, RegistryProvider, S3ClipsSupabaseService as S3ClipsService, S3Service, SKUManagementView, SOPComplianceChart, SSEChatClient, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, ShiftDisplay_default as ShiftDisplay, ShiftsView_default as ShiftsView, SideNavBar, SignupWithInvitation, SilentErrorBoundary, SimpleOnboardingPopup, SingleVideoStream_default as SingleVideoStream, Skeleton, SubscriptionManager, SubscriptionManagerProvider, SupabaseProvider, SupervisorDropdown_default as SupervisorDropdown, SupervisorManagementView_default as SupervisorManagementView, SupervisorService, TargetWorkspaceGrid, TargetsView_default as TargetsView, TeamManagementView_default as TeamManagementView, ThreadSidebar, TicketHistory_default as TicketHistory, TicketHistoryService, TicketsView_default as TicketsView, TimeDisplay_default as TimeDisplay, TimePickerDropdown, Timer_default as Timer, TimezoneProvider, TimezoneService, UserManagementService, UserService, VideoCard, VideoGridView, VideoPlayer, VideoPreloader, WORKSPACE_POSITIONS, WhatsAppShareButton, WorkspaceCard, WorkspaceDetailView_default as WorkspaceDetailView, WorkspaceDisplayNameExample, WorkspaceGrid, WorkspaceGridItem, WorkspaceHealthCard, WorkspaceHealthView_default as WorkspaceHealthView, WorkspaceHistoryCalendar, WorkspaceMetricCards, WorkspaceMetricCardsImpl, WorkspaceMonthlyDataFetcher, WorkspaceMonthlyHistory, WorkspaceMonthlyPdfGenerator, WorkspacePdfExportButton, WorkspacePdfGenerator, WorkspaceWhatsAppShareButton, actionService, apiUtils, authCoreService, authOTPService, authRateLimitService, checkRateLimit2 as checkRateLimit, clearAllRateLimits2 as clearAllRateLimits, clearRateLimit2 as clearRateLimit, clearS3VideoCache, clearS3VideoFromCache, clearWorkspaceDisplayNamesCache, cn, createInvitationService, createLinesService, createStreamProxyHandler, createSupabaseClient, createSupervisorService, createThrottledReload, createUserManagementService, createUserService, dashboardService, deleteThread, forceRefreshWorkspaceDisplayNames, formatDateInZone, formatDateTimeInZone, formatISTDate, formatIdleTime, formatRelativeTime, formatTimeInZone, fromUrlFriendlyName, getAllLineDisplayNames, getAllThreadMessages, getAllWorkspaceDisplayNamesAsync, getAnonClient, getBrowserName, getCameraNumber, getCompanyMetricsTableName, getConfigurableShortWorkspaceDisplayName, getConfigurableWorkspaceDisplayName, getConfiguredLineIds, getCoreSessionRecordingProperties, getCoreSessionReplayUrl, getCurrentShift, getCurrentTimeInZone, getDashboardHeaderTimeInZone, getDaysDifferenceInZone, getDefaultCameraStreamUrl, getDefaultLineId, getDefaultTabForWorkspace, getLineDisplayName, getManufacturingInsights, getMetricsTablePrefix, getNextUpdateInterval, getOperationalDate, getS3SignedUrl, getS3VideoSrc, getShortWorkspaceDisplayName, getShortWorkspaceDisplayNameAsync, getStoredWorkspaceMappings, getSubscriptionManager, getThreadMessages, getUserThreads, getUserThreadsPaginated, getWorkspaceDisplayName, getWorkspaceDisplayNameAsync, getWorkspaceDisplayNamesMap, getWorkspaceFromUrl, getWorkspaceNavigationParams, identifyCoreUser, initializeCoreMixpanel, isLegacyConfiguration, isPrefetchError, isSafari, isTransitionPeriod, isUrlPermanentlyFailed, isValidFactoryViewConfiguration, isValidLineInfoPayload, isValidPrefetchParams, isValidPrefetchStatus, isValidWorkspaceDetailedMetricsPayload, isValidWorkspaceMetricsPayload, isWorkspaceDisplayNamesLoaded, isWorkspaceDisplayNamesLoading, linesService, mergeWithDefaultConfig, migrateLegacyConfiguration, optifyeAgentClient, parseS3Uri, preInitializeWorkspaceDisplayNames, preloadS3Video, preloadS3VideoUrl, preloadS3VideosUrl, preloadVideoUrl, preloadVideosUrl, qualityService, realtimeService, refreshWorkspaceDisplayNames, resetCoreMixpanel, resetFailedUrl, resetSubscriptionManager, s3VideoPreloader, shuffleArray, simulateApiDelay, skuService, startCoreSessionRecording, stopCoreSessionRecording, storeWorkspaceMapping, streamProxyConfig, throttledReloadDashboard, toUrlFriendlyName, trackCoreEvent, trackCorePageView, updateThreadTitle, useAccessControl, useActiveBreaks, useActiveLineId, useAllWorkspaceMetrics, useAnalyticsConfig, useAppTimezone, useAudioService, useAuth, useAuthConfig, useAxelNotifications, useCanSaveTargets, useClipFilter, useClipTypes, useClipTypesWithCounts, useComponentOverride, useCustomConfig, useDashboardConfig, useDashboardMetrics, useDatabaseConfig, useDateFormatter, useDateTimeConfig, useDynamicShiftConfig, useEndpointsConfig, useEntityConfig, useFactoryOverviewMetrics, useFeatureFlags, useFormatNumber, useHasLineAccess, useHistoricWorkspaceMetrics, useHlsStream, useHlsStreamWithCropping, useHookOverride, useHourEndTimer, useHourlyTargetAchievements, useHourlyTargetMisses, useLeaderboardMetrics, useLineDetailedMetrics, useLineKPIs, useLineMetrics, useLineShiftConfig, useLineSupervisor, useLineWorkspaceMetrics, useMessages, useMetrics, useNavigation, useOverrides, usePageOverride, usePrefetchClipCounts, useRealtimeLineMetrics, useRegistry, useSKUs, useSessionKeepAlive, useShiftConfig, useShifts, useSubscriptionManager, useSubscriptionManagerSafe, useSupabase, useSupabaseClient, useTargets, useTeamManagementPermissions, useTheme, useThemeConfig, useThreads, useTicketHistory, useTimezoneContext, useUserLineAccess, useVideoConfig, useWorkspaceConfig, useWorkspaceDetailedMetrics, useWorkspaceDisplayName, useWorkspaceDisplayNames, useWorkspaceDisplayNamesMap, useWorkspaceHealthById, useWorkspaceHealthStatus, useWorkspaceMetrics, useWorkspaceNavigation, useWorkspaceOperators, userService, videoPrefetchManager, videoPreloader, whatsappService, withAccessControl, withAuth, withRegistry, withTimezone, workspaceHealthService, workspaceService };
52000
+ export { ACTION_NAMES, AIAgentView_default as AIAgentView, AcceptInvite, AcceptInviteView_default as AcceptInviteView, AdvancedFilterDialog, AdvancedFilterPanel, AudioService, AuthCallback, AuthCallbackView_default as AuthCallbackView, AuthProvider, AuthService, AuthenticatedBottleneckClipsView, AuthenticatedFactoryView, AuthenticatedHelpView, AuthenticatedHomeView, AuthenticatedShiftsView, AuthenticatedTargetsView, AuthenticatedTicketsView, AuthenticatedWorkspaceHealthView, AxelNotificationPopup, AxelOrb, BackButton, BackButtonMinimal, BarChart, BaseHistoryCalendar, BottleneckClipsModal, BottleneckClipsView_default as BottleneckClipsView, BottlenecksContent, BreakNotificationPopup, CachePrefetchStatus, Card2 as Card, CardContent2 as CardContent, CardDescription2 as CardDescription, CardFooter2 as CardFooter, CardHeader2 as CardHeader, CardTitle2 as CardTitle, ClipFilterProvider, CompactWorkspaceHealthCard, CongratulationsOverlay, CroppedHlsVideoPlayer, CroppedVideoPlayer, CycleTimeChart, CycleTimeOverTimeChart, DEFAULT_ANALYTICS_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_CONFIG, DEFAULT_DATABASE_CONFIG, DEFAULT_DATE_TIME_CONFIG, DEFAULT_ENDPOINTS_CONFIG, DEFAULT_ENTITY_CONFIG, DEFAULT_MAP_VIEW_CONFIG, DEFAULT_SHIFT_CONFIG, DEFAULT_THEME_CONFIG, DEFAULT_VIDEO_CONFIG, DEFAULT_WORKSPACE_CONFIG, DEFAULT_WORKSPACE_POSITIONS, DashboardHeader, DashboardLayout, DashboardOverridesProvider, DashboardProvider, DateDisplay_default as DateDisplay, DateTimeDisplay, DebugAuth, DebugAuthView_default as DebugAuthView, DetailedHealthStatus, DiagnosisVideoModal, EmptyStateMessage, EncouragementOverlay, FactoryView_default as FactoryView, FileManagerFilters, FilterDialogTrigger, FirstTimeLoginDebug, FirstTimeLoginHandler, GaugeChart, GridComponentsPlaceholder, HamburgerButton, Header, HealthStatusGrid, HealthStatusIndicator, HelpView_default as HelpView, HlsVideoPlayer, HomeView_default as HomeView, HourlyOutputChart2 as HourlyOutputChart, ISTTimer_default as ISTTimer, InlineEditableText, InteractiveOnboardingTour, InvitationService, KPICard, KPIDetailView_default as KPIDetailView, KPIGrid, KPIHeader, KPISection, KPIsOverviewView_default as KPIsOverviewView, LINE_1_UUID, LINE_2_UUID, LargeOutputProgressChart, LeaderboardDetailView_default as LeaderboardDetailView, Legend6 as Legend, LineChart, LineHistoryCalendar, LineMonthlyHistory, LineMonthlyPdfGenerator, LinePdfExportButton, LinePdfGenerator, LineWhatsAppShareButton, LinesService, LiveTimer, LoadingInline, LoadingOverlay_default as LoadingOverlay, LoadingPage_default as LoadingPage, LoadingSkeleton, LoadingState, LoginPage, LoginView_default as LoginView, Logo, MainLayout, MapGridView, MetricCard_default as MetricCard, MinimalOnboardingPopup, NewClipsNotification, NoWorkspaceData, OnboardingDemo, OnboardingTour, OptifyeAgentClient, OptifyeLogoLoader_default as OptifyeLogoLoader, OutputProgressChart, PageHeader, PieChart4 as PieChart, PlayPauseIndicator, PrefetchConfigurationError, PrefetchError, PrefetchEvents, PrefetchStatus, PrefetchTimeoutError, ProfileView_default as ProfileView, RegistryProvider, S3ClipsSupabaseService as S3ClipsService, S3Service, SKUManagementView, SOPComplianceChart, SSEChatClient, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, ShiftDisplay_default as ShiftDisplay, ShiftsView_default as ShiftsView, SideNavBar, SignupWithInvitation, SilentErrorBoundary, SimpleOnboardingPopup, SingleVideoStream_default as SingleVideoStream, Skeleton, SubscriptionManager, SubscriptionManagerProvider, SupabaseProvider, SupervisorDropdown_default as SupervisorDropdown, SupervisorManagementView_default as SupervisorManagementView, SupervisorService, TargetWorkspaceGrid, TargetsView_default as TargetsView, TeamManagementView_default as TeamManagementView, ThreadSidebar, TicketHistory_default as TicketHistory, TicketHistoryService, TicketsView_default as TicketsView, TimeDisplay_default as TimeDisplay, TimePickerDropdown, Timer_default as Timer, TimezoneProvider, TimezoneService, UserManagementService, UserService, VideoCard, VideoGridView, VideoPlayer, VideoPreloader, WORKSPACE_POSITIONS, WhatsAppShareButton, WorkspaceCard, WorkspaceDetailView_default as WorkspaceDetailView, WorkspaceDisplayNameExample, WorkspaceGrid, WorkspaceGridItem, WorkspaceHealthCard, WorkspaceHealthView_default as WorkspaceHealthView, WorkspaceHistoryCalendar, WorkspaceMetricCards, WorkspaceMetricCardsImpl, WorkspaceMonthlyDataFetcher, WorkspaceMonthlyHistory, WorkspaceMonthlyPdfGenerator, WorkspacePdfExportButton, WorkspacePdfGenerator, WorkspaceWhatsAppShareButton, actionService, apiUtils, authCoreService, authOTPService, authRateLimitService, checkRateLimit2 as checkRateLimit, clearAllRateLimits2 as clearAllRateLimits, clearRateLimit2 as clearRateLimit, clearS3VideoCache, clearS3VideoFromCache, clearWorkspaceDisplayNamesCache, cn, createInvitationService, createLinesService, createStreamProxyHandler, createSupabaseClient, createSupervisorService, createThrottledReload, createUserManagementService, createUserService, dashboardService, deleteThread, forceRefreshWorkspaceDisplayNames, formatDateInZone, formatDateTimeInZone, formatISTDate, formatIdleTime, formatRelativeTime, formatTimeInZone, fromUrlFriendlyName, getAllLineDisplayNames, getAllThreadMessages, getAllWorkspaceDisplayNamesAsync, getAnonClient, getBrowserName, getCameraNumber, getCompanyMetricsTableName, getConfigurableShortWorkspaceDisplayName, getConfigurableWorkspaceDisplayName, getConfiguredLineIds, getCoreSessionRecordingProperties, getCoreSessionReplayUrl, getCurrentShift, getCurrentTimeInZone, getDashboardHeaderTimeInZone, getDaysDifferenceInZone, getDefaultCameraStreamUrl, getDefaultLineId, getDefaultTabForWorkspace, getLineDisplayName, getManufacturingInsights, getMetricsTablePrefix, getNextUpdateInterval, getOperationalDate, getS3SignedUrl, getS3VideoSrc, getShortWorkspaceDisplayName, getShortWorkspaceDisplayNameAsync, getStoredWorkspaceMappings, getSubscriptionManager, getThreadMessages, getUserThreads, getUserThreadsPaginated, getWorkspaceDisplayName, getWorkspaceDisplayNameAsync, getWorkspaceDisplayNamesMap, getWorkspaceFromUrl, getWorkspaceNavigationParams, identifyCoreUser, initializeCoreMixpanel, isLegacyConfiguration, isPrefetchError, isSafari, isTransitionPeriod, isUrlPermanentlyFailed, isValidFactoryViewConfiguration, isValidLineInfoPayload, isValidPrefetchParams, isValidPrefetchStatus, isValidWorkspaceDetailedMetricsPayload, isValidWorkspaceMetricsPayload, isWorkspaceDisplayNamesLoaded, isWorkspaceDisplayNamesLoading, linesService, mergeWithDefaultConfig, migrateLegacyConfiguration, optifyeAgentClient, parseS3Uri, preInitializeWorkspaceDisplayNames, preloadS3Video, preloadS3VideoUrl, preloadS3VideosUrl, preloadVideoUrl, preloadVideosUrl, qualityService, realtimeService, refreshWorkspaceDisplayNames, resetCoreMixpanel, resetFailedUrl, resetSubscriptionManager, s3VideoPreloader, shuffleArray, simulateApiDelay, skuService, startCoreSessionRecording, stopCoreSessionRecording, storeWorkspaceMapping, streamProxyConfig, throttledReloadDashboard, toUrlFriendlyName, trackCoreEvent, trackCorePageView, updateThreadTitle, useAccessControl, useActiveBreaks, useActiveLineId, useAllWorkspaceMetrics, useAnalyticsConfig, useAppTimezone, useAudioService, useAuth, useAuthConfig, useAxelNotifications, useCanSaveTargets, useClipFilter, useClipTypes, useClipTypesWithCounts, useComponentOverride, useCustomConfig, useDashboardConfig, useDashboardMetrics, useDatabaseConfig, useDateFormatter, useDateTimeConfig, useDynamicShiftConfig, useEndpointsConfig, useEntityConfig, useFactoryOverviewMetrics, useFeatureFlags, useFormatNumber, useHasLineAccess, useHistoricWorkspaceMetrics, useHlsStream, useHlsStreamWithCropping, useHookOverride, useHourEndTimer, useHourlyTargetAchievements, useHourlyTargetMisses, useLeaderboardMetrics, useLineDetailedMetrics, useLineKPIs, useLineMetrics, useLineShiftConfig, useLineSupervisor, useLineWorkspaceMetrics, useMessages, useMetrics, useNavigation, useOverrides, usePageOverride, usePrefetchClipCounts, useRealtimeLineMetrics, useRegistry, useSKUs, useSessionKeepAlive, useShiftConfig, useShifts, useSubscriptionManager, useSubscriptionManagerSafe, useSupabase, useSupabaseClient, useTargets, useTeamManagementPermissions, useTheme, useThemeConfig, useThreads, useTicketHistory, useTimezoneContext, useUserLineAccess, useVideoConfig, useWorkspaceConfig, useWorkspaceDetailedMetrics, useWorkspaceDisplayName, useWorkspaceDisplayNames, useWorkspaceDisplayNamesMap, useWorkspaceHealthById, useWorkspaceHealthStatus, useWorkspaceMetrics, useWorkspaceNavigation, useWorkspaceOperators, userService, videoPrefetchManager, videoPreloader, whatsappService, withAccessControl, withAuth, withRegistry, withTimezone, workspaceHealthService, workspaceService };