@optifye/dashboard-core 4.2.7 → 4.2.8

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.js CHANGED
@@ -7,8 +7,8 @@ var dateFnsTz = require('date-fns-tz');
7
7
  var dateFns = require('date-fns');
8
8
  var mixpanel = require('mixpanel-browser');
9
9
  var supabaseJs = require('@supabase/supabase-js');
10
- var useSWR = require('swr');
11
10
  var Hls2 = require('hls.js');
11
+ var useSWR = require('swr');
12
12
  var motionUtils = require('motion-utils');
13
13
  var motionDom = require('motion-dom');
14
14
  var recharts = require('recharts');
@@ -46,8 +46,8 @@ function _interopNamespace(e) {
46
46
 
47
47
  var React14__namespace = /*#__PURE__*/_interopNamespace(React14);
48
48
  var mixpanel__default = /*#__PURE__*/_interopDefault(mixpanel);
49
- var useSWR__default = /*#__PURE__*/_interopDefault(useSWR);
50
49
  var Hls2__default = /*#__PURE__*/_interopDefault(Hls2);
50
+ var useSWR__default = /*#__PURE__*/_interopDefault(useSWR);
51
51
  var html2canvas__default = /*#__PURE__*/_interopDefault(html2canvas);
52
52
  var jsPDF__default = /*#__PURE__*/_interopDefault(jsPDF);
53
53
  var SelectPrimitive__namespace = /*#__PURE__*/_interopNamespace(SelectPrimitive);
@@ -154,6 +154,12 @@ var DEFAULT_ANALYTICS_CONFIG = {
154
154
  var DEFAULT_AUTH_CONFIG = {
155
155
  // Defaults related to auth providers, redirects etc.
156
156
  };
157
+ var DEFAULT_VIDEO_CONFIG = {
158
+ canvasConfig: {
159
+ fps: 30,
160
+ useRAF: true
161
+ }
162
+ };
157
163
  var LINE_1_UUID = "910a224b-0abc-459a-babb-4c899824cfe7";
158
164
  var DEFAULT_CONFIG = {
159
165
  apiBaseUrl: void 0,
@@ -167,7 +173,8 @@ var DEFAULT_CONFIG = {
167
173
  // Add entity config here
168
174
  shiftConfig: DEFAULT_SHIFT_CONFIG,
169
175
  workspaceConfig: DEFAULT_WORKSPACE_CONFIG,
170
- endpoints: DEFAULT_ENDPOINTS_CONFIG
176
+ endpoints: DEFAULT_ENDPOINTS_CONFIG,
177
+ videoConfig: DEFAULT_VIDEO_CONFIG
171
178
  };
172
179
 
173
180
  // src/lib/utils/config.ts
@@ -299,6 +306,10 @@ function useCustomConfig() {
299
306
  const { customConfig } = useDashboardConfig();
300
307
  return customConfig ?? {};
301
308
  }
309
+ function useVideoConfig() {
310
+ const { videoConfig } = useDashboardConfig();
311
+ return videoConfig ?? DEFAULT_VIDEO_CONFIG;
312
+ }
302
313
 
303
314
  // src/lib/internal/supabaseClientInstance.ts
304
315
  var supabaseInstance = null;
@@ -4247,6 +4258,351 @@ var useWorkspaceOperators = (workspaceId, options) => {
4247
4258
  refetch: fetchData
4248
4259
  };
4249
4260
  };
4261
+
4262
+ // src/lib/utils/dashboardReload.ts
4263
+ var createThrottledReload = (interval = 5e3) => {
4264
+ let last = 0;
4265
+ let queued = false;
4266
+ const doReload = () => {
4267
+ if (typeof window !== "undefined") {
4268
+ window.location.reload();
4269
+ }
4270
+ };
4271
+ return () => {
4272
+ const now2 = Date.now();
4273
+ if (now2 - last >= interval) {
4274
+ last = now2;
4275
+ doReload();
4276
+ } else if (!queued) {
4277
+ queued = true;
4278
+ setTimeout(() => {
4279
+ queued = false;
4280
+ last = Date.now();
4281
+ doReload();
4282
+ }, interval - (now2 - last));
4283
+ }
4284
+ };
4285
+ };
4286
+ var throttledReloadDashboard = createThrottledReload(5e3);
4287
+
4288
+ // src/lib/hooks/useHlsStream.ts
4289
+ var HLS_CONFIG = {
4290
+ maxBufferLength: 8,
4291
+ maxMaxBufferLength: 15,
4292
+ lowLatencyMode: false,
4293
+ enableWorker: true,
4294
+ // Retry + timeout tuned for quick recovery
4295
+ manifestLoadingMaxRetry: 4,
4296
+ levelLoadingMaxRetry: 3,
4297
+ fragLoadingMaxRetry: 4,
4298
+ manifestLoadingRetryDelay: 500,
4299
+ levelLoadingRetryDelay: 500,
4300
+ fragLoadingRetryDelay: 500,
4301
+ manifestLoadingTimeOut: 1e4,
4302
+ levelLoadingTimeOut: 8e3,
4303
+ fragLoadingTimeOut: 1e4,
4304
+ liveSyncDurationCount: 2
4305
+ // Follow live edge aggressively
4306
+ };
4307
+ function useHlsStream(videoRef, { src, shouldPlay, onFatalError }) {
4308
+ const [restartKey, setRestartKey] = React14.useState(0);
4309
+ const hlsRef = React14.useRef(null);
4310
+ const stallCheckIntervalRef = React14.useRef(null);
4311
+ const noProgressTimerRef = React14.useRef(null);
4312
+ const lastTimeUpdateRef = React14.useRef(0);
4313
+ const softRestartCountRef = React14.useRef(0);
4314
+ const isNativeHlsRef = React14.useRef(false);
4315
+ const waitingTimerRef = React14.useRef(null);
4316
+ const cleanup = () => {
4317
+ if (stallCheckIntervalRef.current) {
4318
+ clearInterval(stallCheckIntervalRef.current);
4319
+ stallCheckIntervalRef.current = null;
4320
+ }
4321
+ if (noProgressTimerRef.current) {
4322
+ clearTimeout(noProgressTimerRef.current);
4323
+ noProgressTimerRef.current = null;
4324
+ }
4325
+ if (waitingTimerRef.current) {
4326
+ clearTimeout(waitingTimerRef.current);
4327
+ waitingTimerRef.current = null;
4328
+ }
4329
+ if (hlsRef.current) {
4330
+ hlsRef.current.destroy();
4331
+ hlsRef.current = null;
4332
+ }
4333
+ const video = videoRef.current;
4334
+ if (video) {
4335
+ video.pause();
4336
+ video.removeAttribute("src");
4337
+ video.load();
4338
+ video.removeEventListener("waiting", handleWaiting);
4339
+ video.removeEventListener("timeupdate", handleTimeUpdate);
4340
+ video.removeEventListener("error", handleNativeError);
4341
+ }
4342
+ lastTimeUpdateRef.current = 0;
4343
+ softRestartCountRef.current = 0;
4344
+ };
4345
+ const seekToLiveEdge = () => {
4346
+ const hls = hlsRef.current;
4347
+ const video = videoRef.current;
4348
+ if (!hls || !video) return;
4349
+ if (hls.liveSyncPosition !== null && hls.liveSyncPosition !== void 0) {
4350
+ video.currentTime = hls.liveSyncPosition;
4351
+ } else if (hls.levels?.[hls.currentLevel]?.details) {
4352
+ const levelDetails = hls.levels[hls.currentLevel].details;
4353
+ if (levelDetails && levelDetails.edge !== void 0) {
4354
+ video.currentTime = Math.max(0, levelDetails.edge - 5);
4355
+ }
4356
+ }
4357
+ };
4358
+ const softRestart = (reason) => {
4359
+ console.warn(`[HLS] Soft restart: ${reason}`);
4360
+ const hls = hlsRef.current;
4361
+ if (!hls) return;
4362
+ try {
4363
+ hls.stopLoad();
4364
+ hls.startLoad(-1);
4365
+ seekToLiveEdge();
4366
+ softRestartCountRef.current++;
4367
+ if (softRestartCountRef.current >= 5) {
4368
+ hardRestart(`${reason} (escalated after ${softRestartCountRef.current} soft restarts)`);
4369
+ }
4370
+ } catch (error) {
4371
+ console.error("[HLS] Soft restart failed:", error);
4372
+ hardRestart(`${reason} (soft restart error)`);
4373
+ }
4374
+ };
4375
+ const hardRestart = (reason) => {
4376
+ console.warn(`[HLS] Hard restart: ${reason}`);
4377
+ cleanup();
4378
+ setRestartKey((k) => k + 1);
4379
+ softRestartCountRef.current = 0;
4380
+ if (reason.includes("hard restart") || reason.includes("native video error")) {
4381
+ if (onFatalError) {
4382
+ onFatalError();
4383
+ } else {
4384
+ throttledReloadDashboard();
4385
+ }
4386
+ }
4387
+ };
4388
+ const handleWaiting = () => {
4389
+ if (isNativeHlsRef.current) return;
4390
+ console.log("[HLS] Video waiting (buffer underrun)");
4391
+ if (waitingTimerRef.current) {
4392
+ clearTimeout(waitingTimerRef.current);
4393
+ }
4394
+ waitingTimerRef.current = setTimeout(() => {
4395
+ const video = videoRef.current;
4396
+ if (video && video.readyState < 3) {
4397
+ softRestart("waiting timeout");
4398
+ }
4399
+ }, 1e4);
4400
+ };
4401
+ const handleTimeUpdate = () => {
4402
+ const video = videoRef.current;
4403
+ if (!video) return;
4404
+ lastTimeUpdateRef.current = video.currentTime;
4405
+ if (waitingTimerRef.current) {
4406
+ clearTimeout(waitingTimerRef.current);
4407
+ waitingTimerRef.current = null;
4408
+ }
4409
+ };
4410
+ const handleNativeError = () => {
4411
+ console.error("[HLS] Native video error");
4412
+ hardRestart("native video error");
4413
+ };
4414
+ const startStallDetection = () => {
4415
+ if (isNativeHlsRef.current) return;
4416
+ stallCheckIntervalRef.current = setInterval(() => {
4417
+ const video = videoRef.current;
4418
+ if (!video || video.paused || video.ended) return;
4419
+ const currentTime = video.currentTime;
4420
+ const lastTime = lastTimeUpdateRef.current;
4421
+ if (Math.abs(currentTime - lastTime) < 0.1 && video.readyState >= 2) {
4422
+ console.warn("[HLS] Playback stall detected");
4423
+ if (!noProgressTimerRef.current) {
4424
+ noProgressTimerRef.current = setTimeout(() => {
4425
+ softRestart("playback stall");
4426
+ noProgressTimerRef.current = null;
4427
+ }, 8e3);
4428
+ }
4429
+ } else {
4430
+ if (noProgressTimerRef.current) {
4431
+ clearTimeout(noProgressTimerRef.current);
4432
+ noProgressTimerRef.current = null;
4433
+ }
4434
+ }
4435
+ }, 7e3);
4436
+ };
4437
+ React14.useEffect(() => {
4438
+ if (!src || !shouldPlay) {
4439
+ cleanup();
4440
+ return;
4441
+ }
4442
+ const video = videoRef.current;
4443
+ if (!video) return;
4444
+ isNativeHlsRef.current = video.canPlayType("application/vnd.apple.mpegurl") === "probably";
4445
+ if (Hls2__default.default.isSupported() && !isNativeHlsRef.current) {
4446
+ const hls = new Hls2__default.default(HLS_CONFIG);
4447
+ hlsRef.current = hls;
4448
+ hls.attachMedia(video);
4449
+ hls.loadSource(src);
4450
+ hls.on(Hls2__default.default.Events.ERROR, (_, data) => {
4451
+ if (!data.fatal) return;
4452
+ console.error("[HLS] Fatal error:", data.type, data.details);
4453
+ if (data.response?.code === 404) {
4454
+ hardRestart("404 hard restart");
4455
+ return;
4456
+ }
4457
+ switch (data.type) {
4458
+ case Hls2__default.default.ErrorTypes.NETWORK_ERROR:
4459
+ case Hls2__default.default.ErrorTypes.MEDIA_ERROR:
4460
+ softRestart(`${data.type}: ${data.details}`);
4461
+ break;
4462
+ default:
4463
+ hardRestart(`Fatal ${data.type}: ${data.details}`);
4464
+ break;
4465
+ }
4466
+ });
4467
+ hls.on(Hls2__default.default.Events.MANIFEST_PARSED, () => {
4468
+ video.play().catch((err) => {
4469
+ console.error("[HLS] Play failed:", err);
4470
+ });
4471
+ });
4472
+ video.addEventListener("waiting", handleWaiting);
4473
+ video.addEventListener("timeupdate", handleTimeUpdate);
4474
+ startStallDetection();
4475
+ } else if (isNativeHlsRef.current) {
4476
+ console.log("[HLS] Using native HLS");
4477
+ video.src = src;
4478
+ video.addEventListener("error", handleNativeError);
4479
+ video.play().catch((err) => {
4480
+ console.error("[HLS] Native play failed:", err);
4481
+ });
4482
+ } else {
4483
+ console.error("[HLS] HLS not supported");
4484
+ }
4485
+ return cleanup;
4486
+ }, [src, shouldPlay, restartKey, onFatalError]);
4487
+ return {
4488
+ restartKey,
4489
+ isNativeHls: isNativeHlsRef.current
4490
+ };
4491
+ }
4492
+ function useHlsStreamWithCropping(videoRef, canvasRef, options) {
4493
+ const { src, shouldPlay, cropping, canvasFps = 30, useRAF = true, onFatalError } = options;
4494
+ const animationFrameRef = React14.useRef(null);
4495
+ const intervalRef = React14.useRef(null);
4496
+ const isDrawingRef = React14.useRef(false);
4497
+ const hlsState = useHlsStream(videoRef, { src, shouldPlay, onFatalError });
4498
+ const calculateCropRect = React14.useCallback((video, cropping2) => {
4499
+ const videoWidth = video.videoWidth;
4500
+ const videoHeight = video.videoHeight;
4501
+ const sx = cropping2.x / 100 * videoWidth;
4502
+ const sy = cropping2.y / 100 * videoHeight;
4503
+ const sw = cropping2.width / 100 * videoWidth;
4504
+ const sh = cropping2.height / 100 * videoHeight;
4505
+ return { sx, sy, sw, sh };
4506
+ }, []);
4507
+ const drawFrame = React14.useCallback(() => {
4508
+ const video = videoRef.current;
4509
+ const canvas = canvasRef.current;
4510
+ if (!video || !canvas || !cropping) return;
4511
+ const ctx = canvas.getContext("2d");
4512
+ if (!ctx) return;
4513
+ if (video.readyState < 2) return;
4514
+ try {
4515
+ const videoWidth = video.videoWidth;
4516
+ const videoHeight = video.videoHeight;
4517
+ if (!videoWidth || !videoHeight) return;
4518
+ const { sx, sy, sw, sh } = calculateCropRect(video, cropping);
4519
+ const canvasContainer = canvas.parentElement;
4520
+ if (canvasContainer) {
4521
+ const containerWidth = canvasContainer.clientWidth;
4522
+ const containerHeight = canvasContainer.clientHeight;
4523
+ if (canvas.width !== containerWidth || canvas.height !== containerHeight) {
4524
+ canvas.width = containerWidth;
4525
+ canvas.height = containerHeight;
4526
+ }
4527
+ }
4528
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
4529
+ ctx.drawImage(
4530
+ video,
4531
+ sx,
4532
+ sy,
4533
+ sw,
4534
+ sh,
4535
+ // Source rectangle (cropped portion)
4536
+ 0,
4537
+ 0,
4538
+ canvas.width,
4539
+ canvas.height
4540
+ // Destination rectangle (full canvas)
4541
+ );
4542
+ } catch (err) {
4543
+ console.warn("Canvas drawing error:", err);
4544
+ }
4545
+ }, [videoRef, canvasRef, cropping, calculateCropRect]);
4546
+ const startCanvasRendering = React14.useCallback(() => {
4547
+ if (isDrawingRef.current) return;
4548
+ isDrawingRef.current = true;
4549
+ if (useRAF) {
4550
+ const animate = () => {
4551
+ drawFrame();
4552
+ animationFrameRef.current = requestAnimationFrame(animate);
4553
+ };
4554
+ animate();
4555
+ } else {
4556
+ const frameInterval = 1e3 / canvasFps;
4557
+ intervalRef.current = setInterval(drawFrame, frameInterval);
4558
+ }
4559
+ }, [drawFrame, canvasFps, useRAF]);
4560
+ const stopCanvasRendering = React14.useCallback(() => {
4561
+ isDrawingRef.current = false;
4562
+ if (animationFrameRef.current) {
4563
+ cancelAnimationFrame(animationFrameRef.current);
4564
+ animationFrameRef.current = null;
4565
+ }
4566
+ if (intervalRef.current) {
4567
+ clearInterval(intervalRef.current);
4568
+ intervalRef.current = null;
4569
+ }
4570
+ }, []);
4571
+ React14.useEffect(() => {
4572
+ const video = videoRef.current;
4573
+ if (!video || !cropping || !shouldPlay) {
4574
+ stopCanvasRendering();
4575
+ return;
4576
+ }
4577
+ const handlePlay = () => {
4578
+ if (cropping) {
4579
+ startCanvasRendering();
4580
+ }
4581
+ };
4582
+ const handlePause = () => {
4583
+ stopCanvasRendering();
4584
+ };
4585
+ const handleEnded = () => {
4586
+ stopCanvasRendering();
4587
+ };
4588
+ video.addEventListener("play", handlePlay);
4589
+ video.addEventListener("pause", handlePause);
4590
+ video.addEventListener("ended", handleEnded);
4591
+ if (!video.paused && video.readyState >= 2) {
4592
+ startCanvasRendering();
4593
+ }
4594
+ return () => {
4595
+ stopCanvasRendering();
4596
+ video.removeEventListener("play", handlePlay);
4597
+ video.removeEventListener("pause", handlePause);
4598
+ video.removeEventListener("ended", handleEnded);
4599
+ };
4600
+ }, [videoRef, cropping, shouldPlay, startCanvasRendering, stopCanvasRendering]);
4601
+ return {
4602
+ ...hlsState,
4603
+ isCanvasRendering: isDrawingRef.current
4604
+ };
4605
+ }
4250
4606
  function useThreads() {
4251
4607
  const supabase = _getSupabaseInstance();
4252
4608
  const fetcher = async (key) => {
@@ -4904,349 +5260,118 @@ function useNavigation(customNavigate) {
4904
5260
  };
4905
5261
  attemptNavigation();
4906
5262
  },
4907
- [router$1, customNavigate]
4908
- );
4909
- return {
4910
- pathname,
4911
- query,
4912
- isReady,
4913
- activeLineId,
4914
- activeWorkspaceId,
4915
- isActive,
4916
- isInSection,
4917
- isLineView,
4918
- isWorkspaceView,
4919
- goToDashboard,
4920
- goToWorkspace,
4921
- goToLine,
4922
- goToTargets,
4923
- goToShifts,
4924
- goToLeaderboard,
4925
- goToFactoryView,
4926
- goToProfile,
4927
- navigate
4928
- };
4929
- }
4930
- function useWorkspaceNavigation() {
4931
- const { defaultTimezone } = useDateTimeConfig();
4932
- const getWorkspaceNavigationParams3 = React14.useCallback(
4933
- (workspaceId, options) => {
4934
- let dateToUse = options?.date;
4935
- if (!dateToUse && options?.useCurrentDate) {
4936
- dateToUse = getOperationalDate(defaultTimezone || "UTC");
4937
- }
4938
- return {
4939
- workspaceId,
4940
- date: dateToUse,
4941
- shift: options?.shift,
4942
- sourceType: options?.sourceType
4943
- };
4944
- },
4945
- [defaultTimezone]
4946
- );
4947
- return {
4948
- getWorkspaceNavigationParams: getWorkspaceNavigationParams3
4949
- };
4950
- }
4951
- function useDateFormatter() {
4952
- const { defaultTimezone, defaultLocale, dateFormatOptions, timeFormatOptions, dateTimeFormatOptions } = useDateTimeConfig();
4953
- const formatDate = React14.useCallback(
4954
- (date, formatString) => {
4955
- const dateObj = typeof date === "string" ? dateFns.parseISO(date) : date;
4956
- if (!dateFns.isValid(dateObj)) return "Invalid Date";
4957
- const tz = defaultTimezone || "UTC";
4958
- if (formatString) {
4959
- return dateFnsTz.formatInTimeZone(dateObj, tz, formatString);
4960
- }
4961
- const effectiveOptions = dateFormatOptions || { year: "numeric", month: "short", day: "numeric" };
4962
- return new Intl.DateTimeFormat(defaultLocale || "en-US", effectiveOptions).format(dateObj);
4963
- },
4964
- [defaultTimezone, defaultLocale, dateFormatOptions]
4965
- );
4966
- const formatTime2 = React14.useCallback(
4967
- (date, formatString) => {
4968
- const dateObj = typeof date === "string" ? dateFns.parseISO(date) : date;
4969
- if (!dateFns.isValid(dateObj)) return "Invalid Time";
4970
- const tz = defaultTimezone || "UTC";
4971
- if (formatString) {
4972
- return dateFnsTz.formatInTimeZone(dateObj, tz, formatString);
4973
- }
4974
- const effectiveOptions = timeFormatOptions || { hour: "numeric", minute: "numeric" };
4975
- return new Intl.DateTimeFormat(defaultLocale || "en-US", effectiveOptions).format(dateObj);
4976
- },
4977
- [defaultTimezone, defaultLocale, timeFormatOptions]
4978
- );
4979
- const formatDateTime = React14.useCallback(
4980
- (date, formatString) => {
4981
- const dateObj = typeof date === "string" ? dateFns.parseISO(date) : date;
4982
- if (!dateFns.isValid(dateObj)) return "Invalid Date/Time";
4983
- const tz = defaultTimezone || "UTC";
4984
- if (formatString) {
4985
- return dateFnsTz.formatInTimeZone(dateObj, tz, formatString);
4986
- }
4987
- const effectiveOptions = dateTimeFormatOptions || { year: "numeric", month: "short", day: "numeric", hour: "numeric", minute: "numeric" };
4988
- return new Intl.DateTimeFormat(defaultLocale || "en-US", effectiveOptions).format(dateObj);
4989
- },
4990
- [defaultTimezone, defaultLocale, dateTimeFormatOptions]
4991
- );
4992
- const getNow = React14.useCallback(() => {
4993
- return /* @__PURE__ */ new Date();
4994
- }, []);
4995
- return {
4996
- formatDate,
4997
- formatTime: formatTime2,
4998
- formatDateTime,
4999
- getNow,
5000
- timezone: defaultTimezone || "UTC",
5001
- locale: defaultLocale || "en-US"
5002
- };
5003
- }
5004
- var useFormatNumber = () => {
5005
- const { defaultLocale } = useDateTimeConfig();
5006
- const formatNumber = React14.useCallback(
5007
- (value, options) => {
5008
- try {
5009
- return new Intl.NumberFormat(defaultLocale || "en-US", options).format(value);
5010
- } catch (error) {
5011
- console.error("Error formatting number:", error);
5012
- return String(value);
5013
- }
5014
- },
5015
- [defaultLocale]
5016
- );
5017
- return { formatNumber };
5018
- };
5019
-
5020
- // src/lib/utils/dashboardReload.ts
5021
- var createThrottledReload = (interval = 5e3) => {
5022
- let last = 0;
5023
- let queued = false;
5024
- const doReload = () => {
5025
- if (typeof window !== "undefined") {
5026
- window.location.reload();
5027
- }
5028
- };
5029
- return () => {
5030
- const now2 = Date.now();
5031
- if (now2 - last >= interval) {
5032
- last = now2;
5033
- doReload();
5034
- } else if (!queued) {
5035
- queued = true;
5036
- setTimeout(() => {
5037
- queued = false;
5038
- last = Date.now();
5039
- doReload();
5040
- }, interval - (now2 - last));
5041
- }
5042
- };
5043
- };
5044
- var throttledReloadDashboard = createThrottledReload(5e3);
5045
-
5046
- // src/lib/hooks/useHlsStream.ts
5047
- var HLS_CONFIG = {
5048
- maxBufferLength: 8,
5049
- maxMaxBufferLength: 15,
5050
- lowLatencyMode: false,
5051
- enableWorker: true,
5052
- // Retry + timeout tuned for quick recovery
5053
- manifestLoadingMaxRetry: 4,
5054
- levelLoadingMaxRetry: 3,
5055
- fragLoadingMaxRetry: 4,
5056
- manifestLoadingRetryDelay: 500,
5057
- levelLoadingRetryDelay: 500,
5058
- fragLoadingRetryDelay: 500,
5059
- manifestLoadingTimeOut: 1e4,
5060
- levelLoadingTimeOut: 8e3,
5061
- fragLoadingTimeOut: 1e4,
5062
- liveSyncDurationCount: 2
5063
- // Follow live edge aggressively
5064
- };
5065
- function useHlsStream(videoRef, { src, shouldPlay, onFatalError }) {
5066
- const [restartKey, setRestartKey] = React14.useState(0);
5067
- const hlsRef = React14.useRef(null);
5068
- const stallCheckIntervalRef = React14.useRef(null);
5069
- const noProgressTimerRef = React14.useRef(null);
5070
- const lastTimeUpdateRef = React14.useRef(0);
5071
- const softRestartCountRef = React14.useRef(0);
5072
- const isNativeHlsRef = React14.useRef(false);
5073
- const waitingTimerRef = React14.useRef(null);
5074
- const cleanup = () => {
5075
- if (stallCheckIntervalRef.current) {
5076
- clearInterval(stallCheckIntervalRef.current);
5077
- stallCheckIntervalRef.current = null;
5078
- }
5079
- if (noProgressTimerRef.current) {
5080
- clearTimeout(noProgressTimerRef.current);
5081
- noProgressTimerRef.current = null;
5082
- }
5083
- if (waitingTimerRef.current) {
5084
- clearTimeout(waitingTimerRef.current);
5085
- waitingTimerRef.current = null;
5086
- }
5087
- if (hlsRef.current) {
5088
- hlsRef.current.destroy();
5089
- hlsRef.current = null;
5090
- }
5091
- const video = videoRef.current;
5092
- if (video) {
5093
- video.pause();
5094
- video.removeAttribute("src");
5095
- video.load();
5096
- video.removeEventListener("waiting", handleWaiting);
5097
- video.removeEventListener("timeupdate", handleTimeUpdate);
5098
- video.removeEventListener("error", handleNativeError);
5099
- }
5100
- lastTimeUpdateRef.current = 0;
5101
- softRestartCountRef.current = 0;
5102
- };
5103
- const seekToLiveEdge = () => {
5104
- const hls = hlsRef.current;
5105
- const video = videoRef.current;
5106
- if (!hls || !video) return;
5107
- if (hls.liveSyncPosition !== null && hls.liveSyncPosition !== void 0) {
5108
- video.currentTime = hls.liveSyncPosition;
5109
- } else if (hls.levels?.[hls.currentLevel]?.details) {
5110
- const levelDetails = hls.levels[hls.currentLevel].details;
5111
- if (levelDetails && levelDetails.edge !== void 0) {
5112
- video.currentTime = Math.max(0, levelDetails.edge - 5);
5113
- }
5114
- }
5263
+ [router$1, customNavigate]
5264
+ );
5265
+ return {
5266
+ pathname,
5267
+ query,
5268
+ isReady,
5269
+ activeLineId,
5270
+ activeWorkspaceId,
5271
+ isActive,
5272
+ isInSection,
5273
+ isLineView,
5274
+ isWorkspaceView,
5275
+ goToDashboard,
5276
+ goToWorkspace,
5277
+ goToLine,
5278
+ goToTargets,
5279
+ goToShifts,
5280
+ goToLeaderboard,
5281
+ goToFactoryView,
5282
+ goToProfile,
5283
+ navigate
5115
5284
  };
5116
- const softRestart = (reason) => {
5117
- console.warn(`[HLS] Soft restart: ${reason}`);
5118
- const hls = hlsRef.current;
5119
- if (!hls) return;
5120
- try {
5121
- hls.stopLoad();
5122
- hls.startLoad(-1);
5123
- seekToLiveEdge();
5124
- softRestartCountRef.current++;
5125
- if (softRestartCountRef.current >= 5) {
5126
- hardRestart(`${reason} (escalated after ${softRestartCountRef.current} soft restarts)`);
5285
+ }
5286
+ function useWorkspaceNavigation() {
5287
+ const { defaultTimezone } = useDateTimeConfig();
5288
+ const getWorkspaceNavigationParams3 = React14.useCallback(
5289
+ (workspaceId, options) => {
5290
+ let dateToUse = options?.date;
5291
+ if (!dateToUse && options?.useCurrentDate) {
5292
+ dateToUse = getOperationalDate(defaultTimezone || "UTC");
5127
5293
  }
5128
- } catch (error) {
5129
- console.error("[HLS] Soft restart failed:", error);
5130
- hardRestart(`${reason} (soft restart error)`);
5131
- }
5294
+ return {
5295
+ workspaceId,
5296
+ date: dateToUse,
5297
+ shift: options?.shift,
5298
+ sourceType: options?.sourceType
5299
+ };
5300
+ },
5301
+ [defaultTimezone]
5302
+ );
5303
+ return {
5304
+ getWorkspaceNavigationParams: getWorkspaceNavigationParams3
5132
5305
  };
5133
- const hardRestart = (reason) => {
5134
- console.warn(`[HLS] Hard restart: ${reason}`);
5135
- cleanup();
5136
- setRestartKey((k) => k + 1);
5137
- softRestartCountRef.current = 0;
5138
- if (reason.includes("hard restart") || reason.includes("native video error")) {
5139
- if (onFatalError) {
5140
- onFatalError();
5141
- } else {
5142
- throttledReloadDashboard();
5306
+ }
5307
+ function useDateFormatter() {
5308
+ const { defaultTimezone, defaultLocale, dateFormatOptions, timeFormatOptions, dateTimeFormatOptions } = useDateTimeConfig();
5309
+ const formatDate = React14.useCallback(
5310
+ (date, formatString) => {
5311
+ const dateObj = typeof date === "string" ? dateFns.parseISO(date) : date;
5312
+ if (!dateFns.isValid(dateObj)) return "Invalid Date";
5313
+ const tz = defaultTimezone || "UTC";
5314
+ if (formatString) {
5315
+ return dateFnsTz.formatInTimeZone(dateObj, tz, formatString);
5143
5316
  }
5144
- }
5145
- };
5146
- const handleWaiting = () => {
5147
- if (isNativeHlsRef.current) return;
5148
- console.log("[HLS] Video waiting (buffer underrun)");
5149
- if (waitingTimerRef.current) {
5150
- clearTimeout(waitingTimerRef.current);
5151
- }
5152
- waitingTimerRef.current = setTimeout(() => {
5153
- const video = videoRef.current;
5154
- if (video && video.readyState < 3) {
5155
- softRestart("waiting timeout");
5317
+ const effectiveOptions = dateFormatOptions || { year: "numeric", month: "short", day: "numeric" };
5318
+ return new Intl.DateTimeFormat(defaultLocale || "en-US", effectiveOptions).format(dateObj);
5319
+ },
5320
+ [defaultTimezone, defaultLocale, dateFormatOptions]
5321
+ );
5322
+ const formatTime2 = React14.useCallback(
5323
+ (date, formatString) => {
5324
+ const dateObj = typeof date === "string" ? dateFns.parseISO(date) : date;
5325
+ if (!dateFns.isValid(dateObj)) return "Invalid Time";
5326
+ const tz = defaultTimezone || "UTC";
5327
+ if (formatString) {
5328
+ return dateFnsTz.formatInTimeZone(dateObj, tz, formatString);
5156
5329
  }
5157
- }, 1e4);
5158
- };
5159
- const handleTimeUpdate = () => {
5160
- const video = videoRef.current;
5161
- if (!video) return;
5162
- lastTimeUpdateRef.current = video.currentTime;
5163
- if (waitingTimerRef.current) {
5164
- clearTimeout(waitingTimerRef.current);
5165
- waitingTimerRef.current = null;
5166
- }
5167
- };
5168
- const handleNativeError = () => {
5169
- console.error("[HLS] Native video error");
5170
- hardRestart("native video error");
5171
- };
5172
- const startStallDetection = () => {
5173
- if (isNativeHlsRef.current) return;
5174
- stallCheckIntervalRef.current = setInterval(() => {
5175
- const video = videoRef.current;
5176
- if (!video || video.paused || video.ended) return;
5177
- const currentTime = video.currentTime;
5178
- const lastTime = lastTimeUpdateRef.current;
5179
- if (Math.abs(currentTime - lastTime) < 0.1 && video.readyState >= 2) {
5180
- console.warn("[HLS] Playback stall detected");
5181
- if (!noProgressTimerRef.current) {
5182
- noProgressTimerRef.current = setTimeout(() => {
5183
- softRestart("playback stall");
5184
- noProgressTimerRef.current = null;
5185
- }, 8e3);
5186
- }
5187
- } else {
5188
- if (noProgressTimerRef.current) {
5189
- clearTimeout(noProgressTimerRef.current);
5190
- noProgressTimerRef.current = null;
5191
- }
5330
+ const effectiveOptions = timeFormatOptions || { hour: "numeric", minute: "numeric" };
5331
+ return new Intl.DateTimeFormat(defaultLocale || "en-US", effectiveOptions).format(dateObj);
5332
+ },
5333
+ [defaultTimezone, defaultLocale, timeFormatOptions]
5334
+ );
5335
+ const formatDateTime = React14.useCallback(
5336
+ (date, formatString) => {
5337
+ const dateObj = typeof date === "string" ? dateFns.parseISO(date) : date;
5338
+ if (!dateFns.isValid(dateObj)) return "Invalid Date/Time";
5339
+ const tz = defaultTimezone || "UTC";
5340
+ if (formatString) {
5341
+ return dateFnsTz.formatInTimeZone(dateObj, tz, formatString);
5192
5342
  }
5193
- }, 7e3);
5194
- };
5195
- React14.useEffect(() => {
5196
- if (!src || !shouldPlay) {
5197
- cleanup();
5198
- return;
5199
- }
5200
- const video = videoRef.current;
5201
- if (!video) return;
5202
- isNativeHlsRef.current = video.canPlayType("application/vnd.apple.mpegurl") === "probably";
5203
- if (Hls2__default.default.isSupported() && !isNativeHlsRef.current) {
5204
- const hls = new Hls2__default.default(HLS_CONFIG);
5205
- hlsRef.current = hls;
5206
- hls.attachMedia(video);
5207
- hls.loadSource(src);
5208
- hls.on(Hls2__default.default.Events.ERROR, (_, data) => {
5209
- if (!data.fatal) return;
5210
- console.error("[HLS] Fatal error:", data.type, data.details);
5211
- if (data.response?.code === 404) {
5212
- hardRestart("404 hard restart");
5213
- return;
5214
- }
5215
- switch (data.type) {
5216
- case Hls2__default.default.ErrorTypes.NETWORK_ERROR:
5217
- case Hls2__default.default.ErrorTypes.MEDIA_ERROR:
5218
- softRestart(`${data.type}: ${data.details}`);
5219
- break;
5220
- default:
5221
- hardRestart(`Fatal ${data.type}: ${data.details}`);
5222
- break;
5223
- }
5224
- });
5225
- hls.on(Hls2__default.default.Events.MANIFEST_PARSED, () => {
5226
- video.play().catch((err) => {
5227
- console.error("[HLS] Play failed:", err);
5228
- });
5229
- });
5230
- video.addEventListener("waiting", handleWaiting);
5231
- video.addEventListener("timeupdate", handleTimeUpdate);
5232
- startStallDetection();
5233
- } else if (isNativeHlsRef.current) {
5234
- console.log("[HLS] Using native HLS");
5235
- video.src = src;
5236
- video.addEventListener("error", handleNativeError);
5237
- video.play().catch((err) => {
5238
- console.error("[HLS] Native play failed:", err);
5239
- });
5240
- } else {
5241
- console.error("[HLS] HLS not supported");
5242
- }
5243
- return cleanup;
5244
- }, [src, shouldPlay, restartKey, onFatalError]);
5343
+ const effectiveOptions = dateTimeFormatOptions || { year: "numeric", month: "short", day: "numeric", hour: "numeric", minute: "numeric" };
5344
+ return new Intl.DateTimeFormat(defaultLocale || "en-US", effectiveOptions).format(dateObj);
5345
+ },
5346
+ [defaultTimezone, defaultLocale, dateTimeFormatOptions]
5347
+ );
5348
+ const getNow = React14.useCallback(() => {
5349
+ return /* @__PURE__ */ new Date();
5350
+ }, []);
5245
5351
  return {
5246
- restartKey,
5247
- isNativeHls: isNativeHlsRef.current
5352
+ formatDate,
5353
+ formatTime: formatTime2,
5354
+ formatDateTime,
5355
+ getNow,
5356
+ timezone: defaultTimezone || "UTC",
5357
+ locale: defaultLocale || "en-US"
5248
5358
  };
5249
5359
  }
5360
+ var useFormatNumber = () => {
5361
+ const { defaultLocale } = useDateTimeConfig();
5362
+ const formatNumber = React14.useCallback(
5363
+ (value, options) => {
5364
+ try {
5365
+ return new Intl.NumberFormat(defaultLocale || "en-US", options).format(value);
5366
+ } catch (error) {
5367
+ console.error("Error formatting number:", error);
5368
+ return String(value);
5369
+ }
5370
+ },
5371
+ [defaultLocale]
5372
+ );
5373
+ return { formatNumber };
5374
+ };
5250
5375
 
5251
5376
  // src/lib/utils/api.ts
5252
5377
  var apiUtils = {
@@ -17188,15 +17313,15 @@ var HourlyOutputChart = ({
17188
17313
  renderLegend()
17189
17314
  ] });
17190
17315
  };
17191
- var TREND_STYLES = {
17192
- 0: { arrow: "\u2193", color: "text-red-500 font-bold text-shadow" },
17193
- // Down
17194
- 1: { arrow: "\u2013", color: "text-gray-500 font-bold text-shadow" },
17195
- // Unchanged
17196
- 2: { arrow: "\u2191", color: "text-green-500 font-bold text-shadow" }
17197
- // Up
17198
- };
17199
- var getTrendArrowAndColor = (trend) => TREND_STYLES[trend] || { arrow: "", color: "" };
17316
+ function getTrendArrowAndColor(trend) {
17317
+ if (trend > 0) {
17318
+ return { arrow: "\u2191", color: "text-green-400" };
17319
+ } else if (trend < 0) {
17320
+ return { arrow: "\u2193", color: "text-red-400" };
17321
+ } else {
17322
+ return { arrow: "\u2192", color: "text-gray-400" };
17323
+ }
17324
+ }
17200
17325
  var VideoCard = React14__namespace.default.memo(({
17201
17326
  workspace,
17202
17327
  hlsUrl,
@@ -17204,14 +17329,29 @@ var VideoCard = React14__namespace.default.memo(({
17204
17329
  onClick,
17205
17330
  onFatalError,
17206
17331
  isVeryLowEfficiency = false,
17332
+ cropping,
17333
+ canvasFps = 30,
17334
+ useRAF = true,
17207
17335
  className = ""
17208
17336
  }) => {
17209
17337
  const videoRef = React14.useRef(null);
17210
- useHlsStream(videoRef, {
17211
- src: hlsUrl,
17212
- shouldPlay,
17213
- onFatalError: onFatalError ?? (() => throttledReloadDashboard())
17214
- });
17338
+ const canvasRef = React14.useRef(null);
17339
+ if (cropping) {
17340
+ useHlsStreamWithCropping(videoRef, canvasRef, {
17341
+ src: hlsUrl,
17342
+ shouldPlay,
17343
+ cropping,
17344
+ canvasFps,
17345
+ useRAF,
17346
+ onFatalError: onFatalError ?? (() => throttledReloadDashboard())
17347
+ });
17348
+ } else {
17349
+ useHlsStream(videoRef, {
17350
+ src: hlsUrl,
17351
+ shouldPlay,
17352
+ onFatalError: onFatalError ?? (() => throttledReloadDashboard())
17353
+ });
17354
+ }
17215
17355
  const displayName = getWorkspaceDisplayName(workspace.workspace_name);
17216
17356
  workspace.workspace_uuid || workspace.workspace_name;
17217
17357
  const getEfficiencyOverlayColor = (efficiency) => {
@@ -17266,17 +17406,26 @@ var VideoCard = React14__namespace.default.memo(({
17266
17406
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Camera, { className: "w-6 h-6 text-gray-500" }),
17267
17407
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-gray-500 mt-1", children: "Loading..." })
17268
17408
  ] }) }),
17269
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 z-10", children: /* @__PURE__ */ jsxRuntime.jsx(
17270
- "video",
17271
- {
17272
- ref: videoRef,
17273
- className: "h-full w-full object-cover",
17274
- playsInline: true,
17275
- muted: true,
17276
- disablePictureInPicture: true,
17277
- controlsList: "nodownload noplaybackrate"
17278
- }
17279
- ) }),
17409
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute inset-0 z-10", children: [
17410
+ /* @__PURE__ */ jsxRuntime.jsx(
17411
+ "video",
17412
+ {
17413
+ ref: videoRef,
17414
+ className: `h-full w-full object-cover ${cropping ? "hidden" : ""}`,
17415
+ playsInline: true,
17416
+ muted: true,
17417
+ disablePictureInPicture: true,
17418
+ controlsList: "nodownload noplaybackrate"
17419
+ }
17420
+ ),
17421
+ cropping && /* @__PURE__ */ jsxRuntime.jsx(
17422
+ "canvas",
17423
+ {
17424
+ ref: canvasRef,
17425
+ className: "h-full w-full object-cover"
17426
+ }
17427
+ )
17428
+ ] }),
17280
17429
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: `absolute inset-0 z-20 pointer-events-none ${efficiencyOverlayClass}` }),
17281
17430
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute top-2 right-2 z-30 bg-black/70 backdrop-blur-sm rounded px-2 py-0.5 text-white text-xs font-semibold border border-white/10", children: [
17282
17431
  Math.round(workspace.efficiency),
@@ -17312,7 +17461,7 @@ var VideoCard = React14__namespace.default.memo(({
17312
17461
  }
17313
17462
  );
17314
17463
  }, (prevProps, nextProps) => {
17315
- return prevProps.workspace.workspace_uuid === nextProps.workspace.workspace_uuid && prevProps.workspace.workspace_name === nextProps.workspace.workspace_name && Math.abs(prevProps.workspace.efficiency - nextProps.workspace.efficiency) < 1 && prevProps.hlsUrl === nextProps.hlsUrl && prevProps.shouldPlay === nextProps.shouldPlay;
17464
+ return prevProps.workspace.workspace_uuid === nextProps.workspace.workspace_uuid && prevProps.workspace.workspace_name === nextProps.workspace.workspace_name && Math.abs(prevProps.workspace.efficiency - nextProps.workspace.efficiency) < 1 && prevProps.hlsUrl === nextProps.hlsUrl && prevProps.shouldPlay === nextProps.shouldPlay && prevProps.cropping?.x === nextProps.cropping?.x && prevProps.cropping?.y === nextProps.cropping?.y && prevProps.cropping?.width === nextProps.cropping?.width && prevProps.cropping?.height === nextProps.cropping?.height;
17316
17465
  });
17317
17466
  VideoCard.displayName = "VideoCard";
17318
17467
  var DEFAULT_WORKSPACE_HLS_URLS = {
@@ -17340,6 +17489,8 @@ var VideoGridView = React14__namespace.default.memo(({
17340
17489
  const observerRef = React14.useRef(null);
17341
17490
  const [gridCols, setGridCols] = React14.useState(4);
17342
17491
  const [visibleWorkspaces, setVisibleWorkspaces] = React14.useState(/* @__PURE__ */ new Set());
17492
+ const videoConfig = useVideoConfig();
17493
+ const { cropping, canvasConfig } = videoConfig;
17343
17494
  const mergedVideoSources = {
17344
17495
  defaultHlsUrl: videoSources.defaultHlsUrl || DEFAULT_HLS_URL,
17345
17496
  workspaceHlsUrls: { ...DEFAULT_WORKSPACE_HLS_URLS, ...videoSources.workspaceHlsUrls }
@@ -17348,6 +17499,13 @@ var VideoGridView = React14__namespace.default.memo(({
17348
17499
  const wsName = workspaceName.toUpperCase();
17349
17500
  return mergedVideoSources.workspaceHlsUrls[wsName] || mergedVideoSources.defaultHlsUrl;
17350
17501
  }, [mergedVideoSources]);
17502
+ const getWorkspaceCropping = React14.useCallback((workspaceName) => {
17503
+ if (!cropping) return void 0;
17504
+ if (cropping.workspaceOverrides?.[workspaceName]) {
17505
+ return cropping.workspaceOverrides[workspaceName];
17506
+ }
17507
+ return cropping.default;
17508
+ }, [cropping]);
17351
17509
  const veryLowEfficiencyWorkspaces = React14.useMemo(() => {
17352
17510
  return new Set(
17353
17511
  workspaces.filter((w) => w.efficiency < 50 && w.efficiency >= 10).map((w) => w.workspace_name)
@@ -17486,6 +17644,7 @@ var VideoGridView = React14__namespace.default.memo(({
17486
17644
  const workspaceId = workspace.workspace_uuid || workspace.workspace_name;
17487
17645
  const isVisible = visibleWorkspaces.has(workspaceId);
17488
17646
  const isVeryLowEfficiency = veryLowEfficiencyWorkspaces.has(workspace.workspace_name);
17647
+ const workspaceCropping = getWorkspaceCropping(workspace.workspace_name);
17489
17648
  return /* @__PURE__ */ jsxRuntime.jsx(
17490
17649
  "div",
17491
17650
  {
@@ -17500,7 +17659,10 @@ var VideoGridView = React14__namespace.default.memo(({
17500
17659
  shouldPlay: isVisible,
17501
17660
  onClick: () => handleWorkspaceClick(workspace),
17502
17661
  onFatalError: throttledReloadDashboard,
17503
- isVeryLowEfficiency
17662
+ isVeryLowEfficiency,
17663
+ cropping: workspaceCropping,
17664
+ canvasFps: canvasConfig?.fps,
17665
+ useRAF: canvasConfig?.useRAF
17504
17666
  }
17505
17667
  )
17506
17668
  },
@@ -22327,12 +22489,12 @@ var getEfficiencyColor = (efficiency) => {
22327
22489
  return "bg-[#E34329]/90 hover:bg-[#E34329]/95";
22328
22490
  }
22329
22491
  };
22330
- var TREND_STYLES2 = {
22492
+ var TREND_STYLES = {
22331
22493
  0: { arrow: "\u2193", color: "text-red-500 font-bold text-shadow" },
22332
22494
  1: { arrow: "=", color: "text-gray-500 font-bold text-shadow" },
22333
22495
  2: { arrow: "\u2191", color: "text-green-500 font-bold text-shadow" }
22334
22496
  };
22335
- var getTrendArrowAndColor2 = (trend) => TREND_STYLES2[trend] || { arrow: "", color: "" };
22497
+ var getTrendArrowAndColor2 = (trend) => TREND_STYLES[trend] || { arrow: "", color: "" };
22336
22498
  var ARROW_POSITIONS = {
22337
22499
  top: "-bottom-6",
22338
22500
  "middle-top": "-bottom-6",
@@ -30803,6 +30965,7 @@ exports.DEFAULT_ENDPOINTS_CONFIG = DEFAULT_ENDPOINTS_CONFIG;
30803
30965
  exports.DEFAULT_ENTITY_CONFIG = DEFAULT_ENTITY_CONFIG;
30804
30966
  exports.DEFAULT_SHIFT_CONFIG = DEFAULT_SHIFT_CONFIG;
30805
30967
  exports.DEFAULT_THEME_CONFIG = DEFAULT_THEME_CONFIG;
30968
+ exports.DEFAULT_VIDEO_CONFIG = DEFAULT_VIDEO_CONFIG;
30806
30969
  exports.DEFAULT_WORKSPACE_CONFIG = DEFAULT_WORKSPACE_CONFIG;
30807
30970
  exports.DEFAULT_WORKSPACE_POSITIONS = DEFAULT_WORKSPACE_POSITIONS;
30808
30971
  exports.DashboardHeader = DashboardHeader;
@@ -30992,6 +31155,7 @@ exports.useFeatureFlags = useFeatureFlags;
30992
31155
  exports.useFormatNumber = useFormatNumber;
30993
31156
  exports.useHistoricWorkspaceMetrics = useHistoricWorkspaceMetrics;
30994
31157
  exports.useHlsStream = useHlsStream;
31158
+ exports.useHlsStreamWithCropping = useHlsStreamWithCropping;
30995
31159
  exports.useHookOverride = useHookOverride;
30996
31160
  exports.useLeaderboardMetrics = useLeaderboardMetrics;
30997
31161
  exports.useLineDetailedMetrics = useLineDetailedMetrics;
@@ -31013,6 +31177,7 @@ exports.useTargets = useTargets;
31013
31177
  exports.useTheme = useTheme;
31014
31178
  exports.useThemeConfig = useThemeConfig;
31015
31179
  exports.useThreads = useThreads;
31180
+ exports.useVideoConfig = useVideoConfig;
31016
31181
  exports.useWorkspaceConfig = useWorkspaceConfig;
31017
31182
  exports.useWorkspaceDetailedMetrics = useWorkspaceDetailedMetrics;
31018
31183
  exports.useWorkspaceDisplayName = useWorkspaceDisplayName;