@devalok/shilp-sutra 0.45.0 → 0.46.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/MIGRATION.md +836 -808
  2. package/dist/_chunks/chat.js +157 -156
  3. package/dist/_chunks/chat.js.map +1 -1
  4. package/dist/_chunks/document-preview.js +5 -5
  5. package/dist/_chunks/image-preview.js +7 -7
  6. package/dist/_chunks/mention.js +5 -0
  7. package/dist/_chunks/mention.js.map +1 -0
  8. package/dist/_chunks/success.js +16 -16
  9. package/dist/composed/activity-feed.js +19 -19
  10. package/dist/composed/deadline-indicator.d.ts.map +1 -1
  11. package/dist/composed/deadline-indicator.js +31 -31
  12. package/dist/composed/deadline-indicator.js.map +1 -1
  13. package/dist/composed/emoji-picker.js +15 -15
  14. package/dist/composed/file-preview/video-preview.d.ts.map +1 -1
  15. package/dist/composed/file-preview.js +36 -34
  16. package/dist/composed/file-preview.js.map +1 -1
  17. package/dist/composed/filter-bar.js +1 -1
  18. package/dist/composed/filter-bar.js.map +1 -1
  19. package/dist/composed/multi-select-popover.js +1 -1
  20. package/dist/composed/multi-select-popover.js.map +1 -1
  21. package/dist/composed/rich-chat-input.d.ts.map +1 -1
  22. package/dist/composed/rich-chat-input.js +514 -503
  23. package/dist/composed/rich-chat-input.js.map +1 -1
  24. package/dist/composed/rich-text-editor.d.ts.map +1 -1
  25. package/dist/composed/rich-text-editor.js +144 -143
  26. package/dist/composed/rich-text-editor.js.map +1 -1
  27. package/dist/tokens/base.css +6 -0
  28. package/dist/tokens/index.css +1 -1
  29. package/dist/tokens/semantic.css +1 -1
  30. package/dist/tokens/typography.css +10 -0
  31. package/dist/tokens/utilities.css +6 -0
  32. package/dist/ui/avatar.d.ts.map +1 -1
  33. package/dist/ui/avatar.js +57 -56
  34. package/dist/ui/avatar.js.map +1 -1
  35. package/dist/ui/badge-indicator.js +1 -1
  36. package/dist/ui/badge-indicator.js.map +1 -1
  37. package/dist/ui/chat/message.d.ts.map +1 -1
  38. package/dist/ui/data-table-body.js +27 -27
  39. package/dist/ui/data-table-card.js +20 -20
  40. package/dist/ui/data-table-toolbar.d.ts.map +1 -1
  41. package/dist/ui/data-table-toolbar.js +40 -36
  42. package/dist/ui/data-table-toolbar.js.map +1 -1
  43. package/dist/ui/index.d.ts +1 -0
  44. package/dist/ui/index.d.ts.map +1 -1
  45. package/dist/ui/index.js +37 -36
  46. package/dist/ui/index.js.map +1 -1
  47. package/dist/ui/lib/mention.d.ts +11 -0
  48. package/dist/ui/lib/mention.d.ts.map +1 -0
  49. package/dist/ui/radio.js +1 -1
  50. package/dist/ui/radio.js.map +1 -1
  51. package/dist/ui/sidebar.js +7 -7
  52. package/dist/ui/stat-card.d.ts.map +1 -1
  53. package/dist/ui/stat-card.js +48 -47
  54. package/dist/ui/stat-card.js.map +1 -1
  55. package/dist/ui/surface.d.ts +46 -0
  56. package/dist/ui/surface.d.ts.map +1 -0
  57. package/dist/ui/surface.js +51 -0
  58. package/dist/ui/surface.js.map +1 -0
  59. package/dist/ui/text.js +6 -6
  60. package/dist/ui/text.js.map +1 -1
  61. package/docs/components/ui/chat.md +1 -1
  62. package/docs/components/ui/surface.md +55 -0
  63. package/fonts/Manrope-Variable.woff2 +0 -0
  64. package/llms.txt +2 -1
  65. package/make-kit/components/overview.md +7 -3
  66. package/make-kit/components/surface.md +60 -0
  67. package/mcp-manifest.json +91 -2
  68. package/package.json +11 -1
  69. package/skill/SKILL.md +1 -1
  70. package/skill/references/components.md +2 -1
@@ -6,13 +6,13 @@ import { SimpleTooltip as r } from "./simple-tooltip.js";
6
6
  import * as i from "react";
7
7
  import { jsx as a, jsxs as o } from "react/jsx-runtime";
8
8
  import { IconClock as s } from "@tabler/icons-react";
9
- import { motion as c } from "framer-motion";
9
+ import { motion as c, useReducedMotion as l } from "framer-motion";
10
10
  //#region src/composed/deadline-indicator.tsx
11
- function l(e) {
11
+ function u(e) {
12
12
  let t = Math.abs(e), n = e < 0 ? "Overdue by " : "", r = e >= 0 ? " left" : "";
13
13
  return t < 1 ? e < 0 ? "Overdue" : "Due now" : t < 60 ? `${n}${Math.round(t)}m${r}` : t < 1440 ? `${n}${Math.round(t / 60)}h${r}` : `${n}${Math.round(t / 1440)}d${r}`;
14
14
  }
15
- function u(e) {
15
+ function d(e) {
16
16
  return e.toLocaleDateString(void 0, {
17
17
  month: "short",
18
18
  day: "numeric",
@@ -20,20 +20,20 @@ function u(e) {
20
20
  minute: "2-digit"
21
21
  });
22
22
  }
23
- var d = i.forwardRef(({ deadline: d, warningThreshold: f = 1440, criticalThreshold: p = 240, format: m = "relative", showIcon: h = !1, refreshInterval: g = 6e4, className: _, ...v }, y) => {
24
- let [, b] = i.useReducer((e) => e + 1, 0);
23
+ var f = i.forwardRef(({ deadline: f, warningThreshold: p = 1440, criticalThreshold: m = 240, format: h = "relative", showIcon: g = !1, refreshInterval: _ = 6e4, className: v, ...y }, b) => {
24
+ let [, x] = i.useReducer((e) => e + 1, 0);
25
25
  i.useEffect(() => {
26
- if (!g) return;
27
- let e = setInterval(b, g);
26
+ if (!_) return;
27
+ let e = setInterval(x, _);
28
28
  return () => clearInterval(e);
29
- }, [g]);
30
- let x = i.useMemo(() => d instanceof Date ? d : new Date(d), [d]), S = (x.getTime() - Date.now()) / 6e4, C;
31
- C = S <= 0 ? "text-error-11 font-semibold" : S <= p ? "text-error-11" : S <= f ? "text-warning-11" : "text-success-11";
32
- let w = m === "relative" ? l(S) : u(x), T = S <= 0 || S <= p && S > 0, E = m === "relative", D = x.toLocaleString();
33
- if (T) {
29
+ }, [_]);
30
+ let S = i.useMemo(() => f instanceof Date ? f : new Date(f), [f]), C = (S.getTime() - Date.now()) / 6e4, w;
31
+ w = C <= 0 ? "text-error-11 font-semibold" : C <= m ? "text-error-11" : C <= p ? "text-warning-11" : "text-success-11";
32
+ let T = h === "relative" ? u(C) : d(S), E = C <= 0, D = C <= m && C > 0, O = l(), k = (E || D) && !O, A = h === "relative", j = S.toLocaleString();
33
+ if (k) {
34
34
  let i = /* @__PURE__ */ o(c.span, {
35
- ref: y,
36
- className: t("inline-flex items-center gap-ds-01 font-sans text-ds-sm", C, _),
35
+ ref: b,
36
+ className: t("inline-flex items-center gap-ds-01 font-sans text-ds-sm", w, v),
37
37
  animate: { opacity: [
38
38
  1,
39
39
  .7,
@@ -43,31 +43,31 @@ var d = i.forwardRef(({ deadline: d, warningThreshold: f = 1440, criticalThresho
43
43
  duration: 2,
44
44
  repeat: Infinity
45
45
  },
46
- ...e(v),
47
- children: [h && /* @__PURE__ */ a(n, {
46
+ ...e(y),
47
+ children: [g && /* @__PURE__ */ a(n, {
48
48
  icon: s,
49
49
  size: "xs"
50
- }), w]
50
+ }), T]
51
51
  });
52
- return E ? /* @__PURE__ */ a(r, {
53
- content: D,
52
+ return A ? /* @__PURE__ */ a(r, {
53
+ content: j,
54
54
  children: i
55
55
  }) : i;
56
56
  }
57
- let O = /* @__PURE__ */ o("span", {
58
- ref: y,
59
- className: t("inline-flex items-center gap-ds-01 font-sans text-ds-sm", C, _),
60
- ...v,
61
- children: [h && /* @__PURE__ */ a(n, {
57
+ let M = /* @__PURE__ */ o("span", {
58
+ ref: b,
59
+ className: t("inline-flex items-center gap-ds-01 font-sans text-ds-sm", w, v),
60
+ ...y,
61
+ children: [g && /* @__PURE__ */ a(n, {
62
62
  icon: s,
63
63
  size: "xs"
64
- }), w]
64
+ }), T]
65
65
  });
66
- return E ? /* @__PURE__ */ a(r, {
67
- content: D,
68
- children: O
69
- }) : O;
66
+ return A ? /* @__PURE__ */ a(r, {
67
+ content: j,
68
+ children: M
69
+ }) : M;
70
70
  });
71
- d.displayName = "DeadlineIndicator";
71
+ f.displayName = "DeadlineIndicator";
72
72
  //#endregion
73
- export { d as DeadlineIndicator };
73
+ export { f as DeadlineIndicator };
@@ -1 +1 @@
1
- {"version":3,"file":"deadline-indicator.js","names":[],"sources":["../../src/composed/deadline-indicator.tsx"],"sourcesContent":["'use client'\n\nimport { IconClock } from '@tabler/icons-react'\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { Icon } from '../ui/icon'\nimport { motionProps } from '../ui/lib/motion'\nimport { cn } from '../ui/lib/utils'\nimport { SimpleTooltip } from './simple-tooltip'\n\n// ============================================================\n// Types\n// ============================================================\n\nexport interface DeadlineIndicatorProps extends React.HTMLAttributes<HTMLSpanElement> {\n deadline: Date | string\n /** Minutes before deadline to show warning color @default 1440 (24h) */\n warningThreshold?: number\n /** Minutes before deadline to show critical color @default 240 (4h) */\n criticalThreshold?: number\n /** @default 'relative' */\n format?: 'relative' | 'absolute'\n /** Show clock icon prefix */\n showIcon?: boolean\n /** Auto-refresh interval in ms to keep relative time up to date @default 60000 */\n refreshInterval?: number\n}\n\n// ============================================================\n// Helpers\n// ============================================================\n\nfunction formatRelative(minutesRemaining: number): string {\n const abs = Math.abs(minutesRemaining)\n const prefix = minutesRemaining < 0 ? 'Overdue by ' : ''\n const suffix = minutesRemaining >= 0 ? ' left' : ''\n\n if (abs < 1) return minutesRemaining < 0 ? 'Overdue' : 'Due now'\n if (abs < 60) return `${prefix}${Math.round(abs)}m${suffix}`\n if (abs < 1440) return `${prefix}${Math.round(abs / 60)}h${suffix}`\n return `${prefix}${Math.round(abs / 1440)}d${suffix}`\n}\n\nfunction formatAbsolute(date: Date): string {\n return date.toLocaleDateString(undefined, {\n month: 'short',\n day: 'numeric',\n hour: 'numeric',\n minute: '2-digit',\n })\n}\n\n// ============================================================\n// DeadlineIndicator\n// ============================================================\n\nconst DeadlineIndicator = React.forwardRef<HTMLSpanElement, DeadlineIndicatorProps>(({\n deadline,\n warningThreshold = 1440,\n criticalThreshold = 240,\n format = 'relative',\n showIcon = false,\n refreshInterval = 60000,\n className,\n ...props\n}, ref) => {\n const [, forceUpdate] = React.useReducer((x: number) => x + 1, 0)\n React.useEffect(() => {\n if (!refreshInterval) return\n const id = setInterval(forceUpdate, refreshInterval)\n return () => clearInterval(id)\n }, [refreshInterval])\n\n const deadlineDate = React.useMemo(\n () => (deadline instanceof Date ? deadline : new Date(deadline)),\n [deadline],\n )\n const minutesRemaining = (deadlineDate.getTime() - Date.now()) / 60000\n\n let colorClass: string\n if (minutesRemaining <= 0) {\n colorClass = 'text-error-11 font-semibold'\n } else if (minutesRemaining <= criticalThreshold) {\n colorClass = 'text-error-11'\n } else if (minutesRemaining <= warningThreshold) {\n colorClass = 'text-warning-11'\n } else {\n colorClass = 'text-success-11'\n }\n\n const text = format === 'relative'\n ? formatRelative(minutesRemaining)\n : formatAbsolute(deadlineDate)\n\n const isOverdue = minutesRemaining <= 0\n const isCritical = minutesRemaining <= criticalThreshold && minutesRemaining > 0\n const shouldPulse = isOverdue || isCritical\n\n const showTooltip = format === 'relative'\n const tooltipContent = deadlineDate.toLocaleString()\n\n if (shouldPulse) {\n const inner = (\n <motion.span\n ref={ref}\n className={cn('inline-flex items-center gap-ds-01 font-sans text-ds-sm', colorClass, className)}\n animate={{ opacity: [1, 0.7, 1] }}\n transition={{ duration: 2, repeat: Infinity }}\n {...motionProps(props)}\n >\n {showIcon && <Icon icon={IconClock} size=\"xs\" />}\n {text}\n </motion.span>\n )\n\n return showTooltip ? <SimpleTooltip content={tooltipContent}>{inner}</SimpleTooltip> : inner\n }\n\n const inner = (\n <span\n ref={ref}\n className={cn('inline-flex items-center gap-ds-01 font-sans text-ds-sm', colorClass, className)}\n {...props}\n >\n {showIcon && <Icon icon={IconClock} size=\"xs\" />}\n {text}\n </span>\n )\n\n return showTooltip ? <SimpleTooltip content={tooltipContent}>{inner}</SimpleTooltip> : inner\n})\nDeadlineIndicator.displayName = 'DeadlineIndicator'\n\nexport { DeadlineIndicator }\n"],"mappings":";;;;;;;;;;AAiCA,SAAS,EAAe,GAAkC;CACxD,IAAM,IAAM,KAAK,IAAI,EAAiB,EAChC,IAAS,IAAmB,IAAI,gBAAgB,IAChD,IAAS,KAAoB,IAAI,UAAU;AAKjD,QAHI,IAAM,IAAU,IAAmB,IAAI,YAAY,YACnD,IAAM,KAAW,GAAG,IAAS,KAAK,MAAM,EAAI,CAAC,GAAG,MAChD,IAAM,OAAa,GAAG,IAAS,KAAK,MAAM,IAAM,GAAG,CAAC,GAAG,MACpD,GAAG,IAAS,KAAK,MAAM,IAAM,KAAK,CAAC,GAAG;;AAG/C,SAAS,EAAe,GAAoB;AAC1C,QAAO,EAAK,mBAAmB,KAAA,GAAW;EACxC,OAAO;EACP,KAAK;EACL,MAAM;EACN,QAAQ;EACT,CAAC;;AAOJ,IAAM,IAAoB,EAAM,YAAqD,EACnF,aACA,sBAAmB,MACnB,uBAAoB,KACpB,YAAS,YACT,cAAW,IACX,qBAAkB,KAClB,cACA,GAAG,KACF,MAAQ;CACT,IAAM,GAAG,KAAe,EAAM,YAAY,MAAc,IAAI,GAAG,EAAE;AACjE,GAAM,gBAAgB;AACpB,MAAI,CAAC,EAAiB;EACtB,IAAM,IAAK,YAAY,GAAa,EAAgB;AACpD,eAAa,cAAc,EAAG;IAC7B,CAAC,EAAgB,CAAC;CAErB,IAAM,IAAe,EAAM,cAClB,aAAoB,OAAO,IAAW,IAAI,KAAK,EAAS,EAC/D,CAAC,EAAS,CACX,EACK,KAAoB,EAAa,SAAS,GAAG,KAAK,KAAK,IAAI,KAE7D;AACJ,CAOE,IAPE,KAAoB,IACT,gCACJ,KAAoB,IAChB,kBACJ,KAAoB,IAChB,oBAEA;CAGf,IAAM,IAAO,MAAW,aACpB,EAAe,EAAiB,GAChC,EAAe,EAAa,EAI1B,IAFY,KAAoB,KACnB,KAAoB,KAAqB,IAAmB,GAGzE,IAAc,MAAW,YACzB,IAAiB,EAAa,gBAAgB;AAEpD,KAAI,GAAa;EACf,IAAM,IACJ,kBAAC,EAAO,MAAR;GACO;GACL,WAAW,EAAG,2DAA2D,GAAY,EAAU;GAC/F,SAAS,EAAE,SAAS;IAAC;IAAG;IAAK;IAAE,EAAE;GACjC,YAAY;IAAE,UAAU;IAAG,QAAQ;IAAU;GAC7C,GAAI,EAAY,EAAM;aALxB,CAOG,KAAY,kBAAC,GAAD;IAAM,MAAM;IAAW,MAAK;IAAO,CAAA,EAC/C,EACW;;AAGhB,SAAO,IAAc,kBAAC,GAAD;GAAe,SAAS;aAAiB;GAAsB,CAAA,GAAG;;CAGzF,IAAM,IACJ,kBAAC,QAAD;EACO;EACL,WAAW,EAAG,2DAA2D,GAAY,EAAU;EAC/F,GAAI;YAHN,CAKG,KAAY,kBAAC,GAAD;GAAM,MAAM;GAAW,MAAK;GAAO,CAAA,EAC/C,EACI;;AAGT,QAAO,IAAc,kBAAC,GAAD;EAAe,SAAS;YAAiB;EAAsB,CAAA,GAAG;EACvF;AACF,EAAkB,cAAc"}
1
+ {"version":3,"file":"deadline-indicator.js","names":[],"sources":["../../src/composed/deadline-indicator.tsx"],"sourcesContent":["'use client'\n\nimport { IconClock } from '@tabler/icons-react'\nimport { motion, useReducedMotion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { Icon } from '../ui/icon'\nimport { motionProps } from '../ui/lib/motion'\nimport { cn } from '../ui/lib/utils'\nimport { SimpleTooltip } from './simple-tooltip'\n\n// ============================================================\n// Types\n// ============================================================\n\nexport interface DeadlineIndicatorProps extends React.HTMLAttributes<HTMLSpanElement> {\n deadline: Date | string\n /** Minutes before deadline to show warning color @default 1440 (24h) */\n warningThreshold?: number\n /** Minutes before deadline to show critical color @default 240 (4h) */\n criticalThreshold?: number\n /** @default 'relative' */\n format?: 'relative' | 'absolute'\n /** Show clock icon prefix */\n showIcon?: boolean\n /** Auto-refresh interval in ms to keep relative time up to date @default 60000 */\n refreshInterval?: number\n}\n\n// ============================================================\n// Helpers\n// ============================================================\n\nfunction formatRelative(minutesRemaining: number): string {\n const abs = Math.abs(minutesRemaining)\n const prefix = minutesRemaining < 0 ? 'Overdue by ' : ''\n const suffix = minutesRemaining >= 0 ? ' left' : ''\n\n if (abs < 1) return minutesRemaining < 0 ? 'Overdue' : 'Due now'\n if (abs < 60) return `${prefix}${Math.round(abs)}m${suffix}`\n if (abs < 1440) return `${prefix}${Math.round(abs / 60)}h${suffix}`\n return `${prefix}${Math.round(abs / 1440)}d${suffix}`\n}\n\nfunction formatAbsolute(date: Date): string {\n return date.toLocaleDateString(undefined, {\n month: 'short',\n day: 'numeric',\n hour: 'numeric',\n minute: '2-digit',\n })\n}\n\n// ============================================================\n// DeadlineIndicator\n// ============================================================\n\nconst DeadlineIndicator = React.forwardRef<HTMLSpanElement, DeadlineIndicatorProps>(({\n deadline,\n warningThreshold = 1440,\n criticalThreshold = 240,\n format = 'relative',\n showIcon = false,\n refreshInterval = 60000,\n className,\n ...props\n}, ref) => {\n const [, forceUpdate] = React.useReducer((x: number) => x + 1, 0)\n React.useEffect(() => {\n if (!refreshInterval) return\n const id = setInterval(forceUpdate, refreshInterval)\n return () => clearInterval(id)\n }, [refreshInterval])\n\n const deadlineDate = React.useMemo(\n () => (deadline instanceof Date ? deadline : new Date(deadline)),\n [deadline],\n )\n const minutesRemaining = (deadlineDate.getTime() - Date.now()) / 60000\n\n let colorClass: string\n if (minutesRemaining <= 0) {\n colorClass = 'text-error-11 font-semibold'\n } else if (minutesRemaining <= criticalThreshold) {\n colorClass = 'text-error-11'\n } else if (minutesRemaining <= warningThreshold) {\n colorClass = 'text-warning-11'\n } else {\n colorClass = 'text-success-11'\n }\n\n const text = format === 'relative'\n ? formatRelative(minutesRemaining)\n : formatAbsolute(deadlineDate)\n\n const isOverdue = minutesRemaining <= 0\n const isCritical = minutesRemaining <= criticalThreshold && minutesRemaining > 0\n // Non-transform opacity loop — MotionConfig won't stop it, so drop the pulse\n // under reduced motion. Colour still signals urgency.\n const prefersReduced = useReducedMotion()\n const shouldPulse = (isOverdue || isCritical) && !prefersReduced\n\n const showTooltip = format === 'relative'\n const tooltipContent = deadlineDate.toLocaleString()\n\n if (shouldPulse) {\n const inner = (\n <motion.span\n ref={ref}\n className={cn('inline-flex items-center gap-ds-01 font-sans text-ds-sm', colorClass, className)}\n animate={{ opacity: [1, 0.7, 1] }}\n transition={{ duration: 2, repeat: Infinity }}\n {...motionProps(props)}\n >\n {showIcon && <Icon icon={IconClock} size=\"xs\" />}\n {text}\n </motion.span>\n )\n\n return showTooltip ? <SimpleTooltip content={tooltipContent}>{inner}</SimpleTooltip> : inner\n }\n\n const inner = (\n <span\n ref={ref}\n className={cn('inline-flex items-center gap-ds-01 font-sans text-ds-sm', colorClass, className)}\n {...props}\n >\n {showIcon && <Icon icon={IconClock} size=\"xs\" />}\n {text}\n </span>\n )\n\n return showTooltip ? <SimpleTooltip content={tooltipContent}>{inner}</SimpleTooltip> : inner\n})\nDeadlineIndicator.displayName = 'DeadlineIndicator'\n\nexport { DeadlineIndicator }\n"],"mappings":";;;;;;;;;;AAiCA,SAAS,EAAe,GAAkC;CACxD,IAAM,IAAM,KAAK,IAAI,EAAiB,EAChC,IAAS,IAAmB,IAAI,gBAAgB,IAChD,IAAS,KAAoB,IAAI,UAAU;AAKjD,QAHI,IAAM,IAAU,IAAmB,IAAI,YAAY,YACnD,IAAM,KAAW,GAAG,IAAS,KAAK,MAAM,EAAI,CAAC,GAAG,MAChD,IAAM,OAAa,GAAG,IAAS,KAAK,MAAM,IAAM,GAAG,CAAC,GAAG,MACpD,GAAG,IAAS,KAAK,MAAM,IAAM,KAAK,CAAC,GAAG;;AAG/C,SAAS,EAAe,GAAoB;AAC1C,QAAO,EAAK,mBAAmB,KAAA,GAAW;EACxC,OAAO;EACP,KAAK;EACL,MAAM;EACN,QAAQ;EACT,CAAC;;AAOJ,IAAM,IAAoB,EAAM,YAAqD,EACnF,aACA,sBAAmB,MACnB,uBAAoB,KACpB,YAAS,YACT,cAAW,IACX,qBAAkB,KAClB,cACA,GAAG,KACF,MAAQ;CACT,IAAM,GAAG,KAAe,EAAM,YAAY,MAAc,IAAI,GAAG,EAAE;AACjE,GAAM,gBAAgB;AACpB,MAAI,CAAC,EAAiB;EACtB,IAAM,IAAK,YAAY,GAAa,EAAgB;AACpD,eAAa,cAAc,EAAG;IAC7B,CAAC,EAAgB,CAAC;CAErB,IAAM,IAAe,EAAM,cAClB,aAAoB,OAAO,IAAW,IAAI,KAAK,EAAS,EAC/D,CAAC,EAAS,CACX,EACK,KAAoB,EAAa,SAAS,GAAG,KAAK,KAAK,IAAI,KAE7D;AACJ,CAOE,IAPE,KAAoB,IACT,gCACJ,KAAoB,IAChB,kBACJ,KAAoB,IAChB,oBAEA;CAGf,IAAM,IAAO,MAAW,aACpB,EAAe,EAAiB,GAChC,EAAe,EAAa,EAE1B,IAAY,KAAoB,GAChC,IAAa,KAAoB,KAAqB,IAAmB,GAGzE,IAAiB,GAAkB,EACnC,KAAe,KAAa,MAAe,CAAC,GAE5C,IAAc,MAAW,YACzB,IAAiB,EAAa,gBAAgB;AAEpD,KAAI,GAAa;EACf,IAAM,IACJ,kBAAC,EAAO,MAAR;GACO;GACL,WAAW,EAAG,2DAA2D,GAAY,EAAU;GAC/F,SAAS,EAAE,SAAS;IAAC;IAAG;IAAK;IAAE,EAAE;GACjC,YAAY;IAAE,UAAU;IAAG,QAAQ;IAAU;GAC7C,GAAI,EAAY,EAAM;aALxB,CAOG,KAAY,kBAAC,GAAD;IAAM,MAAM;IAAW,MAAK;IAAO,CAAA,EAC/C,EACW;;AAGhB,SAAO,IAAc,kBAAC,GAAD;GAAe,SAAS;aAAiB;GAAsB,CAAA,GAAG;;CAGzF,IAAM,IACJ,kBAAC,QAAD;EACO;EACL,WAAW,EAAG,2DAA2D,GAAY,EAAU;EAC/F,GAAI;YAHN,CAKG,KAAY,kBAAC,GAAD;GAAM,MAAM;GAAW,MAAK;GAAO,CAAA,EAC/C,EACI;;AAGT,QAAO,IAAc,kBAAC,GAAD;EAAe,SAAS;YAAiB;EAAsB,CAAA,GAAG;EACvF;AACF,EAAkB,cAAc"}
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  import { springs as e } from "../ui/lib/motion.js";
3
3
  import { cn as t } from "../ui/lib/utils.js";
4
- import { i as n, r, t as i } from "../_chunks/popover.js";
5
- import { Skeleton as a } from "../ui/skeleton.js";
4
+ import { Skeleton as n } from "../ui/skeleton.js";
5
+ import { i as r, r as i, t as a } from "../_chunks/popover.js";
6
6
  import * as o from "react";
7
7
  import { jsx as s, jsxs as c } from "react/jsx-runtime";
8
8
  import { AnimatePresence as l, motion as u } from "framer-motion";
@@ -17,11 +17,11 @@ var d = o.lazy(() => import("@emoji-mart/react").then((e) => ({ default: e.defau
17
17
  function p(e) {
18
18
  return e === "auto" ? typeof document > "u" ? "light" : document.documentElement.classList.contains("dark") ? "dark" : "light" : e;
19
19
  }
20
- function m({ onSelect: n, set: r = "native", theme: i = "auto", previewPosition: c = "none", skinTonePosition: m = "search", className: h }) {
20
+ function m({ onSelect: r, set: i = "native", theme: a = "auto", previewPosition: c = "none", skinTonePosition: m = "search", className: h }) {
21
21
  let [g, _] = o.useState(!1), [v, y] = o.useState(null);
22
22
  return o.useEffect(() => {
23
- _(!0), (f[r] ?? f.native)().then((e) => y(e.default));
24
- }, [r]), /* @__PURE__ */ s(l, {
23
+ _(!0), (f[i] ?? f.native)().then((e) => y(e.default));
24
+ }, [i]), /* @__PURE__ */ s(l, {
25
25
  mode: "wait",
26
26
  children: g && v ? /* @__PURE__ */ s(u.div, {
27
27
  initial: {
@@ -37,13 +37,13 @@ function m({ onSelect: n, set: r = "native", theme: i = "auto", previewPosition:
37
37
  children: /* @__PURE__ */ s(o.Suspense, {
38
38
  fallback: /* @__PURE__ */ s("div", {
39
39
  className: t("rounded-surface", h),
40
- children: /* @__PURE__ */ s(a, { className: "h-[435px] w-[352px] rounded-surface" })
40
+ children: /* @__PURE__ */ s(n, { className: "h-[435px] w-[352px] rounded-surface" })
41
41
  }),
42
42
  children: /* @__PURE__ */ s(d, {
43
43
  data: v,
44
- set: r,
45
- onEmojiSelect: n,
46
- theme: p(i),
44
+ set: i,
45
+ onEmojiSelect: r,
46
+ theme: p(a),
47
47
  previewPosition: c,
48
48
  skinTonePosition: m
49
49
  })
@@ -54,25 +54,25 @@ function m({ onSelect: n, set: r = "native", theme: i = "auto", previewPosition:
54
54
  exit: { opacity: 0 },
55
55
  transition: e.snappy,
56
56
  className: t("rounded-surface", h),
57
- children: /* @__PURE__ */ s(a, { className: "h-[435px] w-[352px] rounded-surface" })
57
+ children: /* @__PURE__ */ s(n, { className: "h-[435px] w-[352px] rounded-surface" })
58
58
  }, "skeleton")
59
59
  });
60
60
  }
61
- function h({ children: e, align: t = "start", onSelect: a, set: l, theme: u, previewPosition: d, skinTonePosition: f, className: p }) {
61
+ function h({ children: e, align: t = "start", onSelect: n, set: l, theme: u, previewPosition: d, skinTonePosition: f, className: p }) {
62
62
  let [h, g] = o.useState(!1);
63
- return /* @__PURE__ */ c(i, {
63
+ return /* @__PURE__ */ c(a, {
64
64
  open: h,
65
65
  onOpenChange: g,
66
- children: [/* @__PURE__ */ s(n, {
66
+ children: [/* @__PURE__ */ s(r, {
67
67
  asChild: !0,
68
68
  children: e
69
- }), /* @__PURE__ */ s(r, {
69
+ }), /* @__PURE__ */ s(i, {
70
70
  align: t,
71
71
  className: "w-auto border-none bg-transparent p-0 shadow-none",
72
72
  sideOffset: 8,
73
73
  children: /* @__PURE__ */ s(m, {
74
74
  onSelect: (e) => {
75
- a(e), g(!1);
75
+ n(e), g(!1);
76
76
  },
77
77
  set: l,
78
78
  theme: u,
@@ -1 +1 @@
1
- {"version":3,"file":"video-preview.d.ts","sourceRoot":"","sources":["../../../src/composed/file-preview/video-preview.tsx"],"names":[],"mappings":"AAoBA,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,2CAiNtG"}
1
+ {"version":3,"file":"video-preview.d.ts","sourceRoot":"","sources":["../../../src/composed/file-preview/video-preview.tsx"],"names":[],"mappings":"AAoBA,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,2CAoNtG"}
@@ -2,9 +2,9 @@
2
2
  import { springs as e, tweens as t } from "../ui/lib/motion.js";
3
3
  import { cn as n } from "../ui/lib/utils.js";
4
4
  import { Icon as r } from "../ui/icon.js";
5
- import { n as i } from "../_chunks/badge-group.js";
6
- import { Button as a } from "../ui/button.js";
7
- import { Skeleton as o } from "../ui/skeleton.js";
5
+ import { Skeleton as i } from "../ui/skeleton.js";
6
+ import { n as a } from "../_chunks/badge-group.js";
7
+ import { Button as o } from "../ui/button.js";
8
8
  import { TruncatedText as s } from "../ui/truncated-text.js";
9
9
  import { a as c, n as l, o as u, t as d } from "../_chunks/shared.js";
10
10
  import * as f from "react";
@@ -13,10 +13,10 @@ import { IconDownload as h, IconMaximize as g, IconPlayerPause as _, IconPlayerP
13
13
  import { AnimatePresence as y, motion as b } from "framer-motion";
14
14
  //#region src/composed/file-preview/audio-preview.tsx
15
15
  function x({ url: e, fileName: t, onError: n }) {
16
- let i = f.useRef(null), o = f.useRef(null), [h, g] = f.useState(!1), [y, b] = f.useState(0), [x, S] = f.useState(0), [C, w] = f.useState(0), [T, E] = f.useState(1), [D, O] = f.useState(!1), [k, A] = f.useState(!1);
16
+ let i = f.useRef(null), a = f.useRef(null), [h, g] = f.useState(!1), [y, b] = f.useState(0), [x, S] = f.useState(0), [C, w] = f.useState(0), [T, E] = f.useState(1), [D, O] = f.useState(!1), [k, A] = f.useState(!1);
17
17
  f.useEffect(() => {
18
18
  function e(e) {
19
- let t = o.current;
19
+ let t = a.current;
20
20
  if (!t || !t.contains(document.activeElement) && document.activeElement !== t) return;
21
21
  let n = i.current;
22
22
  if (n) switch (e.key) {
@@ -51,7 +51,7 @@ function x({ url: e, fileName: t, onError: n }) {
51
51
  message: "Could not load audio",
52
52
  url: e
53
53
  }) : /* @__PURE__ */ m("div", {
54
- ref: o,
54
+ ref: a,
55
55
  className: "rounded-surface bg-surface-raised shadow-raised overflow-hidden",
56
56
  tabIndex: -1,
57
57
  children: [
@@ -81,7 +81,7 @@ function x({ url: e, fileName: t, onError: n }) {
81
81
  /* @__PURE__ */ m("div", {
82
82
  className: "flex items-center gap-ds-04 px-ds-05 py-ds-04",
83
83
  children: [
84
- /* @__PURE__ */ p(a, {
84
+ /* @__PURE__ */ p(o, {
85
85
  variant: "solid",
86
86
  size: "icon-sm",
87
87
  onClick: j,
@@ -130,27 +130,27 @@ function x({ url: e, fileName: t, onError: n }) {
130
130
  //#endregion
131
131
  //#region src/composed/file-preview/embed-preview.tsx
132
132
  function S({ url: e, embedUrl: n, onError: r }) {
133
- let [i, a] = f.useState(!1), [s, c] = f.useState(!1);
133
+ let [a, o] = f.useState(!1), [s, c] = f.useState(!1);
134
134
  return f.useEffect(() => {
135
135
  let e = setTimeout(() => {
136
- i || (c(!0), r?.("Embed timed out"));
136
+ a || (c(!0), r?.("Embed timed out"));
137
137
  }, 15e3);
138
138
  return () => clearTimeout(e);
139
- }, [i, r]), s ? /* @__PURE__ */ p(d, {
139
+ }, [a, r]), s ? /* @__PURE__ */ p(d, {
140
140
  message: "Could not load embed",
141
141
  url: e
142
142
  }) : /* @__PURE__ */ m("div", {
143
143
  className: "relative w-full rounded-control overflow-hidden",
144
144
  style: { aspectRatio: "16 / 9" },
145
- children: [!i && /* @__PURE__ */ p(o, { className: "absolute inset-0" }), /* @__PURE__ */ p(b.iframe, {
145
+ children: [!a && /* @__PURE__ */ p(i, { className: "absolute inset-0" }), /* @__PURE__ */ p(b.iframe, {
146
146
  src: n,
147
147
  title: "Embedded content",
148
- onLoad: () => a(!0),
148
+ onLoad: () => o(!0),
149
149
  onError: () => {
150
150
  c(!0), r?.("Embed failed to load");
151
151
  },
152
152
  initial: { opacity: 0 },
153
- animate: { opacity: +!!i },
153
+ animate: { opacity: +!!a },
154
154
  transition: t.fade,
155
155
  className: "absolute inset-0 h-full w-full border-none",
156
156
  allowFullScreen: !0
@@ -168,8 +168,10 @@ var C = [
168
168
  2
169
169
  ];
170
170
  function w({ url: n, onError: i }) {
171
- let a = f.useRef(null), o = f.useRef(null), [s, h] = f.useState(!1), [x, S] = f.useState(0), [w, T] = f.useState(0), [E, D] = f.useState(0), [O, k] = f.useState(!1), [A, j] = f.useState(!1), [M, N] = f.useState(!0), [P, F] = f.useState(1), I = f.useRef(void 0);
172
- f.useEffect(() => () => clearTimeout(I.current), []), f.useEffect(() => {
171
+ let a = f.useRef(null), o = f.useRef(null), [s, h] = f.useState(!1), [x, S] = f.useState(0), [w, T] = f.useState(0), [E, D] = f.useState(0), [O, k] = f.useState(!1), [A, j] = f.useState(!1), [M, N] = f.useState(!0), [P, F] = f.useState(1), I = f.useRef(P);
172
+ I.current = P;
173
+ let L = f.useRef(void 0);
174
+ f.useEffect(() => () => clearTimeout(L.current), []), f.useEffect(() => {
173
175
  function e(e) {
174
176
  let t = o.current;
175
177
  if (!t || !t.contains(document.activeElement) && document.activeElement !== t) return;
@@ -198,27 +200,27 @@ function w({ url: n, onError: i }) {
198
200
  e.preventDefault(), n.requestFullscreen?.();
199
201
  break;
200
202
  case ">":
201
- e.preventDefault(), L(1);
203
+ e.preventDefault(), R(1);
202
204
  break;
203
205
  case "<":
204
- e.preventDefault(), L(-1);
206
+ e.preventDefault(), R(-1);
205
207
  break;
206
208
  }
207
209
  }
208
210
  return document.addEventListener("keydown", e), () => document.removeEventListener("keydown", e);
209
211
  }, []);
210
- function L(e) {
211
- let t = C.indexOf(P), n = C[Math.max(0, Math.min(C.length - 1, t + e))];
212
+ function R(e) {
213
+ let t = C.indexOf(I.current), n = C[Math.max(0, Math.min(C.length - 1, t + e))];
212
214
  F(n), a.current && (a.current.playbackRate = n);
213
215
  }
214
- function R() {
216
+ function z() {
215
217
  a.current && (s ? a.current.pause() : a.current.play(), h(!s));
216
218
  }
217
- function z(e) {
219
+ function B(e) {
218
220
  !a.current || !w || (a.current.currentTime = e / 100 * w);
219
221
  }
220
- function B() {
221
- N(!0), clearTimeout(I.current), s && (I.current = setTimeout(() => N(!1), 3e3));
222
+ function V() {
223
+ N(!0), clearTimeout(L.current), s && (L.current = setTimeout(() => N(!1), 3e3));
222
224
  }
223
225
  return A ? /* @__PURE__ */ p(d, {
224
226
  message: "Could not load video",
@@ -226,7 +228,7 @@ function w({ url: n, onError: i }) {
226
228
  }) : /* @__PURE__ */ m("div", {
227
229
  ref: o,
228
230
  className: "group relative rounded-control bg-black overflow-hidden",
229
- onMouseMove: B,
231
+ onMouseMove: V,
230
232
  onMouseLeave: () => s && N(!1),
231
233
  tabIndex: -1,
232
234
  children: [
@@ -234,7 +236,7 @@ function w({ url: n, onError: i }) {
234
236
  ref: a,
235
237
  src: n,
236
238
  className: "max-h-[70vh] w-full",
237
- onClick: R,
239
+ onClick: z,
238
240
  muted: O,
239
241
  onTimeUpdate: () => {
240
242
  a.current && (D(a.current.currentTime), a.current.duration && S(a.current.currentTime / a.current.duration * 100));
@@ -258,7 +260,7 @@ function w({ url: n, onError: i }) {
258
260
  scale: 1
259
261
  },
260
262
  transition: e.snappy,
261
- onClick: R,
263
+ onClick: z,
262
264
  className: "absolute inset-0 flex items-center justify-center bg-black/30",
263
265
  "aria-label": "Play video",
264
266
  children: /* @__PURE__ */ p("div", {
@@ -290,14 +292,14 @@ function w({ url: n, onError: i }) {
290
292
  value: x,
291
293
  max: 100,
292
294
  step: .1,
293
- onValueChange: z,
295
+ onValueChange: B,
294
296
  tone: "dark",
295
297
  ariaLabel: "Video progress"
296
298
  }), /* @__PURE__ */ m("div", {
297
299
  className: "flex items-center gap-ds-03",
298
300
  children: [
299
301
  /* @__PURE__ */ p("button", {
300
- onClick: R,
302
+ onClick: z,
301
303
  className: "text-white hover:text-white/80",
302
304
  "aria-label": s ? "Pause" : "Play",
303
305
  title: s ? "Pause" : "Play",
@@ -321,7 +323,7 @@ function w({ url: n, onError: i }) {
321
323
  variant: "dark"
322
324
  }),
323
325
  /* @__PURE__ */ m("span", {
324
- className: "text-[11px] font-mono text-white/70 tabular-nums",
326
+ className: "text-ds-sm font-mono text-white/70 tabular-nums",
325
327
  children: [
326
328
  u(E),
327
329
  " / ",
@@ -330,8 +332,8 @@ function w({ url: n, onError: i }) {
330
332
  }),
331
333
  /* @__PURE__ */ p("div", { className: "flex-1" }),
332
334
  /* @__PURE__ */ m("button", {
333
- onClick: () => L(1),
334
- className: "text-[11px] font-mono text-white/70 hover:text-white px-1 rounded-control-inner hover:bg-white/10 transition-colors",
335
+ onClick: () => R(1),
336
+ className: "text-ds-sm font-mono text-white/70 hover:text-white px-1 rounded-control-inner hover:bg-white/10 transition-colors",
335
337
  "aria-label": `Playback speed: ${P}x`,
336
338
  children: [P, "x"]
337
339
  }),
@@ -394,7 +396,7 @@ function O(e) {
394
396
  return r ? `https://www.loom.com/embed/${r[1]}` : e;
395
397
  }
396
398
  function k({ url: e, type: t, mimeType: c, alt: l, initialPage: u = 1, fileName: d, fileSize: g, onError: _, className: v, ...y }) {
397
- let b = t ?? D(e, c), C = /* @__PURE__ */ p(o, { className: "h-64 w-full rounded-control" });
399
+ let b = t ?? D(e, c), C = /* @__PURE__ */ p(i, { className: "h-64 w-full rounded-control" });
398
400
  return /* @__PURE__ */ m("div", {
399
401
  className: n("flex flex-col gap-ds-03", v),
400
402
  ...y,
@@ -405,7 +407,7 @@ function k({ url: e, type: t, mimeType: c, alt: l, initialPage: u = 1, fileName:
405
407
  mode: "middle",
406
408
  className: "min-w-0 text-ds-sm font-semibold text-surface-fg",
407
409
  children: d
408
- }), g && /* @__PURE__ */ p(i, {
410
+ }), g && /* @__PURE__ */ p(a, {
409
411
  variant: "subtle",
410
412
  size: "xs",
411
413
  children: g
@@ -443,7 +445,7 @@ function k({ url: e, type: t, mimeType: c, alt: l, initialPage: u = 1, fileName:
443
445
  }),
444
446
  b !== "audio" && /* @__PURE__ */ p("div", {
445
447
  className: "flex justify-end",
446
- children: /* @__PURE__ */ p(a, {
448
+ children: /* @__PURE__ */ p(o, {
447
449
  variant: "ghost",
448
450
  size: "xs",
449
451
  startIcon: /* @__PURE__ */ p(r, { icon: h }),
@@ -1 +1 @@
1
- {"version":3,"file":"file-preview.js","names":[],"sources":["../../src/composed/file-preview/audio-preview.tsx","../../src/composed/file-preview/embed-preview.tsx","../../src/composed/file-preview/video-preview.tsx","../../src/composed/file-preview.tsx"],"sourcesContent":["'use client'\n\nimport { IconPlayerPause,IconPlayerPlay } from '@tabler/icons-react'\nimport * as React from 'react'\n\nimport { Button } from '../../ui/button'\nimport { Icon } from '../../ui/icon'\nimport { TruncatedText } from '../../ui/truncated-text'\nimport { ErrorFallback, formatTime,MediaSlider,VolumeControl } from './shared'\n\n// ============================================================\n// Audio Preview — Branded mini-player (Spotify/SoundCloud style)\n// ============================================================\n\nexport default function AudioPreview({ url, fileName, onError }: { url: string; fileName?: string; onError?: (msg: string) => void }) {\n const audioRef = React.useRef<HTMLAudioElement>(null)\n const containerRef = React.useRef<HTMLDivElement>(null)\n const [playing, setPlaying] = React.useState(false)\n const [progress, setProgress] = React.useState(0)\n const [duration, setDuration] = React.useState(0)\n const [currentTime, setCurrentTime] = React.useState(0)\n const [volume, setVolume] = React.useState(1)\n const [muted, setMuted] = React.useState(false)\n const [error, setError] = React.useState(false)\n\n // Keyboard shortcuts\n React.useEffect(() => {\n function handleKeyDown(e: KeyboardEvent) {\n const el = containerRef.current\n if (!el || !el.contains(document.activeElement) && document.activeElement !== el) return\n const a = audioRef.current\n if (!a) return\n\n switch (e.key) {\n case ' ':\n e.preventDefault()\n setPlaying(prev => {\n if (prev) a.pause(); else a.play()\n return !prev\n })\n break\n case 'ArrowRight':\n e.preventDefault()\n a.currentTime = Math.min(a.duration || 0, a.currentTime + 5)\n break\n case 'ArrowLeft':\n e.preventDefault()\n a.currentTime = Math.max(0, a.currentTime - 5)\n break\n case 'm':\n e.preventDefault()\n setMuted(prev => {\n a.muted = !prev\n return !prev\n })\n break\n }\n }\n document.addEventListener('keydown', handleKeyDown)\n return () => document.removeEventListener('keydown', handleKeyDown)\n \n }, [])\n\n function togglePlay() {\n if (!audioRef.current) return\n if (playing) audioRef.current.pause()\n else audioRef.current.play()\n setPlaying(!playing)\n }\n\n function handleSeek(nextProgress: number) {\n if (!audioRef.current || !duration) return\n audioRef.current.currentTime = (nextProgress / 100) * duration\n }\n\n function toggleMute() {\n if (!audioRef.current) return\n const next = !muted\n setMuted(next)\n audioRef.current.muted = next\n }\n\n if (error) return <ErrorFallback message=\"Could not load audio\" url={url} />\n\n return (\n <div ref={containerRef} className=\"rounded-surface bg-surface-raised shadow-raised overflow-hidden\" tabIndex={-1}>\n {/* eslint-disable-next-line jsx-a11y/media-has-caption */}\n <audio\n ref={audioRef}\n src={url}\n onTimeUpdate={() => {\n if (!audioRef.current) return\n setCurrentTime(audioRef.current.currentTime)\n if (audioRef.current.duration) setProgress((audioRef.current.currentTime / audioRef.current.duration) * 100)\n }}\n onLoadedMetadata={() => setDuration(audioRef.current?.duration ?? 0)}\n onEnded={() => setPlaying(false)}\n onError={() => { setError(true); onError?.('Audio failed to load') }}\n />\n\n {/* Progress bar — full width at top (Spotify style) */}\n <div className=\"px-ds-03 pt-ds-02\">\n <MediaSlider\n value={progress}\n max={100}\n step={0.1}\n onValueChange={handleSeek}\n tone=\"light\"\n ariaLabel=\"Audio progress\"\n />\n </div>\n\n {/* Controls */}\n <div className=\"flex items-center gap-ds-04 px-ds-05 py-ds-04\">\n <Button\n variant=\"solid\"\n size=\"icon-sm\"\n onClick={togglePlay}\n aria-label={playing ? 'Pause' : 'Play'}\n title={playing ? 'Pause' : 'Play'}\n className=\"shrink-0\"\n >\n {playing ? (\n <Icon icon={IconPlayerPause} size=\"sm\" />\n ) : (\n <Icon icon={IconPlayerPlay} size=\"sm\" className=\"ml-0.5\" />\n )}\n </Button>\n\n {/* File name + time */}\n <div className=\"flex-1 min-w-0\">\n {fileName && (\n <TruncatedText as=\"p\" mode=\"middle\" className=\"text-ds-sm font-semibold text-surface-fg\">\n {fileName}\n </TruncatedText>\n )}\n <p className=\"text-ds-xs font-mono text-surface-fg-muted tabular-nums\">\n {formatTime(currentTime)} / {formatTime(duration)}\n </p>\n </div>\n\n {/* Volume */}\n <VolumeControl\n volume={volume}\n muted={muted}\n onVolumeChange={(v) => {\n setVolume(v)\n if (audioRef.current) audioRef.current.volume = v\n if (v > 0 && muted) {\n setMuted(false)\n if (audioRef.current) audioRef.current.muted = false\n }\n }}\n onMuteToggle={toggleMute}\n variant=\"light\"\n />\n </div>\n </div>\n )\n}\n","'use client'\n\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { tweens } from '../../ui/lib/motion'\nimport { Skeleton } from '../../ui/skeleton'\nimport { ErrorFallback } from './shared'\n\n// ============================================================\n// Embed Preview — 16:9 with error timeout\n// ============================================================\n\nexport default function EmbedPreview({ url, embedUrl, onError }: { url: string; embedUrl: string; onError?: (msg: string) => void }) {\n const [loaded, setLoaded] = React.useState(false)\n const [error, setError] = React.useState(false)\n\n // Timeout — if iframe doesn't load in 15s, show error\n React.useEffect(() => {\n const timer = setTimeout(() => {\n if (!loaded) { setError(true); onError?.('Embed timed out') }\n }, 15000)\n return () => clearTimeout(timer)\n }, [loaded, onError])\n\n if (error) return <ErrorFallback message=\"Could not load embed\" url={url} />\n\n return (\n <div className=\"relative w-full rounded-control overflow-hidden\" style={{ aspectRatio: '16 / 9' }}>\n {!loaded && <Skeleton className=\"absolute inset-0\" />}\n <motion.iframe\n src={embedUrl}\n title=\"Embedded content\"\n onLoad={() => setLoaded(true)}\n onError={() => { setError(true); onError?.('Embed failed to load') }}\n initial={{ opacity: 0 }}\n animate={{ opacity: loaded ? 1 : 0 }}\n transition={tweens.fade}\n className=\"absolute inset-0 h-full w-full border-none\"\n allowFullScreen\n />\n </div>\n )\n}\n","'use client'\n\nimport {\n IconMaximize,\n IconPlayerPause,\n IconPlayerPlay,\n} from '@tabler/icons-react'\nimport { AnimatePresence,motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { Icon } from '../../ui/icon'\nimport { springs, tweens } from '../../ui/lib/motion'\nimport { ErrorFallback, formatTime,MediaSlider,VolumeControl } from './shared'\n\n// ============================================================\n// Video Preview — Custom player with DS styling\n// ============================================================\n\nconst PLAYBACK_RATES = [0.5, 0.75, 1, 1.25, 1.5, 2] as const\n\nexport default function VideoPreview({ url, onError }: { url: string; onError?: (msg: string) => void }) {\n const videoRef = React.useRef<HTMLVideoElement>(null)\n const containerRef = React.useRef<HTMLDivElement>(null)\n const [playing, setPlaying] = React.useState(false)\n const [progress, setProgress] = React.useState(0)\n const [duration, setDuration] = React.useState(0)\n const [currentTime, setCurrentTime] = React.useState(0)\n const [muted, setMuted] = React.useState(false)\n const [error, setError] = React.useState(false)\n const [showControls, setShowControls] = React.useState(true)\n const [playbackRate, setPlaybackRate] = React.useState(1)\n const hideTimer = React.useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n\n React.useEffect(() => {\n return () => clearTimeout(hideTimer.current)\n }, [])\n\n // Keyboard shortcuts — YouTube style\n React.useEffect(() => {\n function handleKeyDown(e: KeyboardEvent) {\n const el = containerRef.current\n if (!el || !el.contains(document.activeElement) && document.activeElement !== el) return\n const v = videoRef.current\n if (!v) return\n\n switch (e.key) {\n case ' ':\n case 'k':\n e.preventDefault()\n setPlaying(prev => {\n if (prev) v.pause(); else v.play()\n return !prev\n })\n break\n case 'ArrowRight':\n e.preventDefault()\n v.currentTime = Math.min(v.duration, v.currentTime + 5)\n break\n case 'ArrowLeft':\n e.preventDefault()\n v.currentTime = Math.max(0, v.currentTime - 5)\n break\n case 'j':\n e.preventDefault()\n v.currentTime = Math.max(0, v.currentTime - 10)\n break\n case 'l':\n e.preventDefault()\n v.currentTime = Math.min(v.duration, v.currentTime + 10)\n break\n case 'm':\n e.preventDefault()\n setMuted(prev => {\n v.muted = !prev\n return !prev\n })\n break\n case 'f':\n e.preventDefault()\n v.requestFullscreen?.()\n break\n case '>':\n e.preventDefault()\n cyclePlaybackRate(1)\n break\n case '<':\n e.preventDefault()\n cyclePlaybackRate(-1)\n break\n }\n }\n document.addEventListener('keydown', handleKeyDown)\n return () => document.removeEventListener('keydown', handleKeyDown)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n function cyclePlaybackRate(direction: 1 | -1) {\n const idx = PLAYBACK_RATES.indexOf(playbackRate as typeof PLAYBACK_RATES[number])\n const nextIdx = Math.max(0, Math.min(PLAYBACK_RATES.length - 1, idx + direction))\n const next = PLAYBACK_RATES[nextIdx]\n setPlaybackRate(next)\n if (videoRef.current) videoRef.current.playbackRate = next\n }\n\n function togglePlay() {\n if (!videoRef.current) return\n if (playing) videoRef.current.pause()\n else videoRef.current.play()\n setPlaying(!playing)\n }\n\n function handleSeek(nextProgress: number) {\n if (!videoRef.current || !duration) return\n videoRef.current.currentTime = (nextProgress / 100) * duration\n }\n\n function handleMouseMove() {\n setShowControls(true)\n clearTimeout(hideTimer.current)\n if (playing) {\n hideTimer.current = setTimeout(() => setShowControls(false), 3000)\n }\n }\n\n if (error) return <ErrorFallback message=\"Could not load video\" url={url} />\n\n return (\n <div\n ref={containerRef}\n className=\"group relative rounded-control bg-black overflow-hidden\"\n onMouseMove={handleMouseMove}\n onMouseLeave={() => playing && setShowControls(false)}\n tabIndex={-1}\n >\n {/* eslint-disable-next-line jsx-a11y/media-has-caption */}\n <video\n ref={videoRef}\n src={url}\n className=\"max-h-[70vh] w-full\"\n onClick={togglePlay}\n muted={muted}\n onTimeUpdate={() => {\n if (!videoRef.current) return\n setCurrentTime(videoRef.current.currentTime)\n if (videoRef.current.duration) setProgress((videoRef.current.currentTime / videoRef.current.duration) * 100)\n }}\n onLoadedMetadata={() => setDuration(videoRef.current?.duration ?? 0)}\n onEnded={() => { setPlaying(false); setShowControls(true) }}\n onError={() => { setError(true); onError?.('Video failed to load') }}\n playsInline\n />\n\n {/* Play button overlay — shown when paused */}\n {!playing && (\n <motion.button\n initial={{ opacity: 0, scale: 0.8 }}\n animate={{ opacity: 1, scale: 1 }}\n transition={springs.snappy}\n onClick={togglePlay}\n className=\"absolute inset-0 flex items-center justify-center bg-black/30\"\n aria-label=\"Play video\"\n >\n <div className=\"flex h-14 w-14 items-center justify-center rounded-pill bg-white/90 shadow-floating\">\n <Icon icon={IconPlayerPlay} size=\"xl\" className=\"text-surface-fg ml-0.5\" />\n </div>\n </motion.button>\n )}\n\n {/* Bottom controls — auto-hide after 3s during playback */}\n <AnimatePresence>\n {showControls && duration > 0 && (\n <motion.div\n initial={{ opacity: 0, y: 10 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: 10 }}\n transition={tweens.fade}\n className=\"absolute bottom-0 left-0 right-0 bg-linear-to-t from-black/80 to-transparent px-ds-04 pb-ds-04 pt-ds-08\"\n >\n {/* Progress bar */}\n <MediaSlider\n className=\"mb-ds-03\"\n value={progress}\n max={100}\n step={0.1}\n onValueChange={handleSeek}\n tone=\"dark\"\n ariaLabel=\"Video progress\"\n />\n\n {/* Controls row */}\n <div className=\"flex items-center gap-ds-03\">\n <button onClick={togglePlay} className=\"text-white hover:text-white/80\" aria-label={playing ? 'Pause' : 'Play'} title={playing ? 'Pause' : 'Play'}>\n {playing ? <Icon icon={IconPlayerPause} size=\"lg\" /> : <Icon icon={IconPlayerPlay} size=\"lg\" />}\n </button>\n <VolumeControl\n volume={muted ? 0 : 1}\n muted={muted}\n onVolumeChange={(v) => {\n if (videoRef.current) videoRef.current.volume = v\n if (v > 0 && muted) { setMuted(false); if (videoRef.current) videoRef.current.muted = false }\n }}\n onMuteToggle={() => { setMuted(!muted); if (videoRef.current) videoRef.current.muted = !muted }}\n variant=\"dark\"\n />\n <span className=\"text-[11px] font-mono text-white/70 tabular-nums\">\n {formatTime(currentTime)} / {formatTime(duration)}\n </span>\n <div className=\"flex-1\" />\n <button\n onClick={() => cyclePlaybackRate(1)}\n className=\"text-[11px] font-mono text-white/70 hover:text-white px-1 rounded-control-inner hover:bg-white/10 transition-colors\"\n aria-label={`Playback speed: ${playbackRate}x`}\n >\n {playbackRate}x\n </button>\n <button\n onClick={() => videoRef.current?.requestFullscreen?.()}\n className=\"text-white hover:text-white/80\"\n aria-label=\"Fullscreen (F)\"\n title=\"Fullscreen\"\n >\n <Icon icon={IconMaximize} size=\"sm\" />\n </button>\n </div>\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n )\n}\n","'use client'\n\nimport { IconDownload } from '@tabler/icons-react'\nimport * as React from 'react'\n\nimport { Badge } from '../ui/badge'\nimport { Button } from '../ui/button'\nimport { Icon } from '../ui/icon'\nimport { cn } from '../ui/lib/utils'\nimport { Skeleton } from '../ui/skeleton'\nimport { TruncatedText } from '../ui/truncated-text'\n\n// Sub-components: direct imports for non-peer-dep renderers,\n// React.lazy for renderers that pull in optional peer deps\n// (react-zoom-pan-pinch, react-pdf) to preserve code-splitting.\nconst LazyImagePreview = React.lazy(() => import('./file-preview/image-preview'))\nconst LazyDocumentPreview = React.lazy(() => import('./file-preview/document-preview'))\nimport AudioPreview from './file-preview/audio-preview'\nimport EmbedPreview from './file-preview/embed-preview'\nimport VideoPreview from './file-preview/video-preview'\n\n// ============================================================\n// Types\n// ============================================================\n\ntype FileType = 'image' | 'pdf' | 'video' | 'audio' | 'embed'\n\nexport interface FilePreviewProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onError'> {\n url: string\n /** Auto-detected from URL/mimeType if omitted */\n type?: FileType\n mimeType?: string\n alt?: string\n /** PDF starting page @default 1 */\n initialPage?: number\n /** File name for display */\n fileName?: string\n /** File size for display (e.g. '2.4 MB') */\n fileSize?: string\n /** Called on errors (broken URL, CORS, timeout) */\n onError?: (error: string) => void\n}\n\n// ============================================================\n// Type Detection\n// ============================================================\n\nfunction detectType(url: string, mimeType?: string): FileType {\n if (mimeType) {\n if (mimeType.startsWith('image/')) return 'image'\n if (mimeType === 'application/pdf') return 'pdf'\n if (mimeType.startsWith('video/')) return 'video'\n if (mimeType.startsWith('audio/')) return 'audio'\n }\n\n const ext = url.split('?')[0].split('.').pop()?.toLowerCase()\n if (['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'avif', 'bmp'].includes(ext ?? '')) return 'image'\n if (ext === 'pdf') return 'pdf'\n if (['mp4', 'webm', 'ogg', 'mov'].includes(ext ?? '')) return 'video'\n if (['mp3', 'wav', 'ogg', 'aac', 'flac'].includes(ext ?? '')) return 'audio'\n\n if (url.includes('figma.com') || url.includes('loom.com') || url.includes('youtube.com') || url.includes('youtu.be') || url.includes('vimeo.com')) {\n return 'embed'\n }\n\n return 'image'\n}\n\nfunction getEmbedUrl(url: string): string {\n const ytMatch = url.match(/(?:youtube\\.com\\/watch\\?v=|youtu\\.be\\/)([^&?]+)/)\n if (ytMatch) return `https://www.youtube.com/embed/${ytMatch[1]}`\n\n const vimeoMatch = url.match(/vimeo\\.com\\/(\\d+)/)\n if (vimeoMatch) return `https://player.vimeo.com/video/${vimeoMatch[1]}`\n\n if (url.includes('figma.com')) return `https://www.figma.com/embed?embed_host=share&url=${encodeURIComponent(url)}`\n\n const loomMatch = url.match(/loom\\.com\\/share\\/([^?]+)/)\n if (loomMatch) return `https://www.loom.com/embed/${loomMatch[1]}`\n\n return url\n}\n\n// ============================================================\n// FilePreview — Main component (router)\n// ============================================================\n\nfunction FilePreview({\n url,\n type: typeProp,\n mimeType,\n alt,\n initialPage = 1,\n fileName,\n fileSize,\n onError,\n className,\n ...props\n}: FilePreviewProps) {\n const type = typeProp ?? detectType(url, mimeType)\n const fallback = <Skeleton className=\"h-64 w-full rounded-control\" />\n\n return (\n <div className={cn('flex flex-col gap-ds-03', className)} {...props}>\n {/* File info bar */}\n {(fileName || fileSize) && (\n <div className=\"flex items-center gap-ds-03\">\n {fileName && (\n <TruncatedText mode=\"middle\" className=\"min-w-0 text-ds-sm font-semibold text-surface-fg\">\n {fileName}\n </TruncatedText>\n )}\n {fileSize && <Badge variant=\"subtle\" size=\"xs\">{fileSize}</Badge>}\n </div>\n )}\n\n {/* Preview */}\n {type === 'image' && (\n <React.Suspense fallback={fallback}>\n <LazyImagePreview url={url} alt={alt} onError={onError} />\n </React.Suspense>\n )}\n {type === 'pdf' && (\n <React.Suspense fallback={fallback}>\n <LazyDocumentPreview url={url} initialPage={initialPage} onError={onError} />\n </React.Suspense>\n )}\n {type === 'video' && <VideoPreview url={url} onError={onError} />}\n {type === 'audio' && <AudioPreview url={url} fileName={fileName} onError={onError} />}\n {type === 'embed' && <EmbedPreview url={url} embedUrl={getEmbedUrl(url)} onError={onError} />}\n\n {/* Download — not shown for audio (it has its own layout) */}\n {type !== 'audio' && (\n <div className=\"flex justify-end\">\n <Button variant=\"ghost\" size=\"xs\" startIcon={<Icon icon={IconDownload} />} asChild>\n <a href={url} download target=\"_blank\" rel=\"noopener noreferrer\">Download</a>\n </Button>\n </div>\n )}\n </div>\n )\n}\n\nexport { FilePreview }\nexport type { FileType }\n"],"mappings":"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"}
1
+ {"version":3,"file":"file-preview.js","names":[],"sources":["../../src/composed/file-preview/audio-preview.tsx","../../src/composed/file-preview/embed-preview.tsx","../../src/composed/file-preview/video-preview.tsx","../../src/composed/file-preview.tsx"],"sourcesContent":["'use client'\n\nimport { IconPlayerPause,IconPlayerPlay } from '@tabler/icons-react'\nimport * as React from 'react'\n\nimport { Button } from '../../ui/button'\nimport { Icon } from '../../ui/icon'\nimport { TruncatedText } from '../../ui/truncated-text'\nimport { ErrorFallback, formatTime,MediaSlider,VolumeControl } from './shared'\n\n// ============================================================\n// Audio Preview — Branded mini-player (Spotify/SoundCloud style)\n// ============================================================\n\nexport default function AudioPreview({ url, fileName, onError }: { url: string; fileName?: string; onError?: (msg: string) => void }) {\n const audioRef = React.useRef<HTMLAudioElement>(null)\n const containerRef = React.useRef<HTMLDivElement>(null)\n const [playing, setPlaying] = React.useState(false)\n const [progress, setProgress] = React.useState(0)\n const [duration, setDuration] = React.useState(0)\n const [currentTime, setCurrentTime] = React.useState(0)\n const [volume, setVolume] = React.useState(1)\n const [muted, setMuted] = React.useState(false)\n const [error, setError] = React.useState(false)\n\n // Keyboard shortcuts\n React.useEffect(() => {\n function handleKeyDown(e: KeyboardEvent) {\n const el = containerRef.current\n if (!el || !el.contains(document.activeElement) && document.activeElement !== el) return\n const a = audioRef.current\n if (!a) return\n\n switch (e.key) {\n case ' ':\n e.preventDefault()\n setPlaying(prev => {\n if (prev) a.pause(); else a.play()\n return !prev\n })\n break\n case 'ArrowRight':\n e.preventDefault()\n a.currentTime = Math.min(a.duration || 0, a.currentTime + 5)\n break\n case 'ArrowLeft':\n e.preventDefault()\n a.currentTime = Math.max(0, a.currentTime - 5)\n break\n case 'm':\n e.preventDefault()\n setMuted(prev => {\n a.muted = !prev\n return !prev\n })\n break\n }\n }\n document.addEventListener('keydown', handleKeyDown)\n return () => document.removeEventListener('keydown', handleKeyDown)\n \n }, [])\n\n function togglePlay() {\n if (!audioRef.current) return\n if (playing) audioRef.current.pause()\n else audioRef.current.play()\n setPlaying(!playing)\n }\n\n function handleSeek(nextProgress: number) {\n if (!audioRef.current || !duration) return\n audioRef.current.currentTime = (nextProgress / 100) * duration\n }\n\n function toggleMute() {\n if (!audioRef.current) return\n const next = !muted\n setMuted(next)\n audioRef.current.muted = next\n }\n\n if (error) return <ErrorFallback message=\"Could not load audio\" url={url} />\n\n return (\n <div ref={containerRef} className=\"rounded-surface bg-surface-raised shadow-raised overflow-hidden\" tabIndex={-1}>\n {/* eslint-disable-next-line jsx-a11y/media-has-caption */}\n <audio\n ref={audioRef}\n src={url}\n onTimeUpdate={() => {\n if (!audioRef.current) return\n setCurrentTime(audioRef.current.currentTime)\n if (audioRef.current.duration) setProgress((audioRef.current.currentTime / audioRef.current.duration) * 100)\n }}\n onLoadedMetadata={() => setDuration(audioRef.current?.duration ?? 0)}\n onEnded={() => setPlaying(false)}\n onError={() => { setError(true); onError?.('Audio failed to load') }}\n />\n\n {/* Progress bar — full width at top (Spotify style) */}\n <div className=\"px-ds-03 pt-ds-02\">\n <MediaSlider\n value={progress}\n max={100}\n step={0.1}\n onValueChange={handleSeek}\n tone=\"light\"\n ariaLabel=\"Audio progress\"\n />\n </div>\n\n {/* Controls */}\n <div className=\"flex items-center gap-ds-04 px-ds-05 py-ds-04\">\n <Button\n variant=\"solid\"\n size=\"icon-sm\"\n onClick={togglePlay}\n aria-label={playing ? 'Pause' : 'Play'}\n title={playing ? 'Pause' : 'Play'}\n className=\"shrink-0\"\n >\n {playing ? (\n <Icon icon={IconPlayerPause} size=\"sm\" />\n ) : (\n <Icon icon={IconPlayerPlay} size=\"sm\" className=\"ml-0.5\" />\n )}\n </Button>\n\n {/* File name + time */}\n <div className=\"flex-1 min-w-0\">\n {fileName && (\n <TruncatedText as=\"p\" mode=\"middle\" className=\"text-ds-sm font-semibold text-surface-fg\">\n {fileName}\n </TruncatedText>\n )}\n <p className=\"text-ds-xs font-mono text-surface-fg-muted tabular-nums\">\n {formatTime(currentTime)} / {formatTime(duration)}\n </p>\n </div>\n\n {/* Volume */}\n <VolumeControl\n volume={volume}\n muted={muted}\n onVolumeChange={(v) => {\n setVolume(v)\n if (audioRef.current) audioRef.current.volume = v\n if (v > 0 && muted) {\n setMuted(false)\n if (audioRef.current) audioRef.current.muted = false\n }\n }}\n onMuteToggle={toggleMute}\n variant=\"light\"\n />\n </div>\n </div>\n )\n}\n","'use client'\n\nimport { motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { tweens } from '../../ui/lib/motion'\nimport { Skeleton } from '../../ui/skeleton'\nimport { ErrorFallback } from './shared'\n\n// ============================================================\n// Embed Preview — 16:9 with error timeout\n// ============================================================\n\nexport default function EmbedPreview({ url, embedUrl, onError }: { url: string; embedUrl: string; onError?: (msg: string) => void }) {\n const [loaded, setLoaded] = React.useState(false)\n const [error, setError] = React.useState(false)\n\n // Timeout — if iframe doesn't load in 15s, show error\n React.useEffect(() => {\n const timer = setTimeout(() => {\n if (!loaded) { setError(true); onError?.('Embed timed out') }\n }, 15000)\n return () => clearTimeout(timer)\n }, [loaded, onError])\n\n if (error) return <ErrorFallback message=\"Could not load embed\" url={url} />\n\n return (\n <div className=\"relative w-full rounded-control overflow-hidden\" style={{ aspectRatio: '16 / 9' }}>\n {!loaded && <Skeleton className=\"absolute inset-0\" />}\n <motion.iframe\n src={embedUrl}\n title=\"Embedded content\"\n onLoad={() => setLoaded(true)}\n onError={() => { setError(true); onError?.('Embed failed to load') }}\n initial={{ opacity: 0 }}\n animate={{ opacity: loaded ? 1 : 0 }}\n transition={tweens.fade}\n className=\"absolute inset-0 h-full w-full border-none\"\n allowFullScreen\n />\n </div>\n )\n}\n","'use client'\n\nimport {\n IconMaximize,\n IconPlayerPause,\n IconPlayerPlay,\n} from '@tabler/icons-react'\nimport { AnimatePresence,motion } from 'framer-motion'\nimport * as React from 'react'\n\nimport { Icon } from '../../ui/icon'\nimport { springs, tweens } from '../../ui/lib/motion'\nimport { ErrorFallback, formatTime,MediaSlider,VolumeControl } from './shared'\n\n// ============================================================\n// Video Preview — Custom player with DS styling\n// ============================================================\n\nconst PLAYBACK_RATES = [0.5, 0.75, 1, 1.25, 1.5, 2] as const\n\nexport default function VideoPreview({ url, onError }: { url: string; onError?: (msg: string) => void }) {\n const videoRef = React.useRef<HTMLVideoElement>(null)\n const containerRef = React.useRef<HTMLDivElement>(null)\n const [playing, setPlaying] = React.useState(false)\n const [progress, setProgress] = React.useState(0)\n const [duration, setDuration] = React.useState(0)\n const [currentTime, setCurrentTime] = React.useState(0)\n const [muted, setMuted] = React.useState(false)\n const [error, setError] = React.useState(false)\n const [showControls, setShowControls] = React.useState(true)\n const [playbackRate, setPlaybackRate] = React.useState(1)\n // Kept current every render so the mount-time keydown handler (empty deps)\n // reads the live rate instead of the stale initial 1x. See #91.\n const playbackRateRef = React.useRef(playbackRate)\n playbackRateRef.current = playbackRate\n const hideTimer = React.useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n\n React.useEffect(() => {\n return () => clearTimeout(hideTimer.current)\n }, [])\n\n // Keyboard shortcuts — YouTube style\n React.useEffect(() => {\n function handleKeyDown(e: KeyboardEvent) {\n const el = containerRef.current\n if (!el || !el.contains(document.activeElement) && document.activeElement !== el) return\n const v = videoRef.current\n if (!v) return\n\n switch (e.key) {\n case ' ':\n case 'k':\n e.preventDefault()\n setPlaying(prev => {\n if (prev) v.pause(); else v.play()\n return !prev\n })\n break\n case 'ArrowRight':\n e.preventDefault()\n v.currentTime = Math.min(v.duration, v.currentTime + 5)\n break\n case 'ArrowLeft':\n e.preventDefault()\n v.currentTime = Math.max(0, v.currentTime - 5)\n break\n case 'j':\n e.preventDefault()\n v.currentTime = Math.max(0, v.currentTime - 10)\n break\n case 'l':\n e.preventDefault()\n v.currentTime = Math.min(v.duration, v.currentTime + 10)\n break\n case 'm':\n e.preventDefault()\n setMuted(prev => {\n v.muted = !prev\n return !prev\n })\n break\n case 'f':\n e.preventDefault()\n v.requestFullscreen?.()\n break\n case '>':\n e.preventDefault()\n cyclePlaybackRate(1)\n break\n case '<':\n e.preventDefault()\n cyclePlaybackRate(-1)\n break\n }\n }\n document.addEventListener('keydown', handleKeyDown)\n return () => document.removeEventListener('keydown', handleKeyDown)\n }, [])\n\n function cyclePlaybackRate(direction: 1 | -1) {\n const idx = PLAYBACK_RATES.indexOf(playbackRateRef.current as typeof PLAYBACK_RATES[number])\n const nextIdx = Math.max(0, Math.min(PLAYBACK_RATES.length - 1, idx + direction))\n const next = PLAYBACK_RATES[nextIdx]\n setPlaybackRate(next)\n if (videoRef.current) videoRef.current.playbackRate = next\n }\n\n function togglePlay() {\n if (!videoRef.current) return\n if (playing) videoRef.current.pause()\n else videoRef.current.play()\n setPlaying(!playing)\n }\n\n function handleSeek(nextProgress: number) {\n if (!videoRef.current || !duration) return\n videoRef.current.currentTime = (nextProgress / 100) * duration\n }\n\n function handleMouseMove() {\n setShowControls(true)\n clearTimeout(hideTimer.current)\n if (playing) {\n hideTimer.current = setTimeout(() => setShowControls(false), 3000)\n }\n }\n\n if (error) return <ErrorFallback message=\"Could not load video\" url={url} />\n\n return (\n <div\n ref={containerRef}\n className=\"group relative rounded-control bg-black overflow-hidden\"\n onMouseMove={handleMouseMove}\n onMouseLeave={() => playing && setShowControls(false)}\n tabIndex={-1}\n >\n {/* eslint-disable-next-line jsx-a11y/media-has-caption */}\n <video\n ref={videoRef}\n src={url}\n className=\"max-h-[70vh] w-full\"\n onClick={togglePlay}\n muted={muted}\n onTimeUpdate={() => {\n if (!videoRef.current) return\n setCurrentTime(videoRef.current.currentTime)\n if (videoRef.current.duration) setProgress((videoRef.current.currentTime / videoRef.current.duration) * 100)\n }}\n onLoadedMetadata={() => setDuration(videoRef.current?.duration ?? 0)}\n onEnded={() => { setPlaying(false); setShowControls(true) }}\n onError={() => { setError(true); onError?.('Video failed to load') }}\n playsInline\n />\n\n {/* Play button overlay — shown when paused */}\n {!playing && (\n <motion.button\n initial={{ opacity: 0, scale: 0.8 }}\n animate={{ opacity: 1, scale: 1 }}\n transition={springs.snappy}\n onClick={togglePlay}\n className=\"absolute inset-0 flex items-center justify-center bg-black/30\"\n aria-label=\"Play video\"\n >\n <div className=\"flex h-14 w-14 items-center justify-center rounded-pill bg-white/90 shadow-floating\">\n <Icon icon={IconPlayerPlay} size=\"xl\" className=\"text-surface-fg ml-0.5\" />\n </div>\n </motion.button>\n )}\n\n {/* Bottom controls — auto-hide after 3s during playback */}\n <AnimatePresence>\n {showControls && duration > 0 && (\n <motion.div\n initial={{ opacity: 0, y: 10 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: 10 }}\n transition={tweens.fade}\n className=\"absolute bottom-0 left-0 right-0 bg-linear-to-t from-black/80 to-transparent px-ds-04 pb-ds-04 pt-ds-08\"\n >\n {/* Progress bar */}\n <MediaSlider\n className=\"mb-ds-03\"\n value={progress}\n max={100}\n step={0.1}\n onValueChange={handleSeek}\n tone=\"dark\"\n ariaLabel=\"Video progress\"\n />\n\n {/* Controls row */}\n <div className=\"flex items-center gap-ds-03\">\n <button onClick={togglePlay} className=\"text-white hover:text-white/80\" aria-label={playing ? 'Pause' : 'Play'} title={playing ? 'Pause' : 'Play'}>\n {playing ? <Icon icon={IconPlayerPause} size=\"lg\" /> : <Icon icon={IconPlayerPlay} size=\"lg\" />}\n </button>\n <VolumeControl\n volume={muted ? 0 : 1}\n muted={muted}\n onVolumeChange={(v) => {\n if (videoRef.current) videoRef.current.volume = v\n if (v > 0 && muted) { setMuted(false); if (videoRef.current) videoRef.current.muted = false }\n }}\n onMuteToggle={() => { setMuted(!muted); if (videoRef.current) videoRef.current.muted = !muted }}\n variant=\"dark\"\n />\n <span className=\"text-ds-sm font-mono text-white/70 tabular-nums\">\n {formatTime(currentTime)} / {formatTime(duration)}\n </span>\n <div className=\"flex-1\" />\n <button\n onClick={() => cyclePlaybackRate(1)}\n className=\"text-ds-sm font-mono text-white/70 hover:text-white px-1 rounded-control-inner hover:bg-white/10 transition-colors\"\n aria-label={`Playback speed: ${playbackRate}x`}\n >\n {playbackRate}x\n </button>\n <button\n onClick={() => videoRef.current?.requestFullscreen?.()}\n className=\"text-white hover:text-white/80\"\n aria-label=\"Fullscreen (F)\"\n title=\"Fullscreen\"\n >\n <Icon icon={IconMaximize} size=\"sm\" />\n </button>\n </div>\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n )\n}\n","'use client'\n\nimport { IconDownload } from '@tabler/icons-react'\nimport * as React from 'react'\n\nimport { Badge } from '../ui/badge'\nimport { Button } from '../ui/button'\nimport { Icon } from '../ui/icon'\nimport { cn } from '../ui/lib/utils'\nimport { Skeleton } from '../ui/skeleton'\nimport { TruncatedText } from '../ui/truncated-text'\n\n// Sub-components: direct imports for non-peer-dep renderers,\n// React.lazy for renderers that pull in optional peer deps\n// (react-zoom-pan-pinch, react-pdf) to preserve code-splitting.\nconst LazyImagePreview = React.lazy(() => import('./file-preview/image-preview'))\nconst LazyDocumentPreview = React.lazy(() => import('./file-preview/document-preview'))\nimport AudioPreview from './file-preview/audio-preview'\nimport EmbedPreview from './file-preview/embed-preview'\nimport VideoPreview from './file-preview/video-preview'\n\n// ============================================================\n// Types\n// ============================================================\n\ntype FileType = 'image' | 'pdf' | 'video' | 'audio' | 'embed'\n\nexport interface FilePreviewProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onError'> {\n url: string\n /** Auto-detected from URL/mimeType if omitted */\n type?: FileType\n mimeType?: string\n alt?: string\n /** PDF starting page @default 1 */\n initialPage?: number\n /** File name for display */\n fileName?: string\n /** File size for display (e.g. '2.4 MB') */\n fileSize?: string\n /** Called on errors (broken URL, CORS, timeout) */\n onError?: (error: string) => void\n}\n\n// ============================================================\n// Type Detection\n// ============================================================\n\nfunction detectType(url: string, mimeType?: string): FileType {\n if (mimeType) {\n if (mimeType.startsWith('image/')) return 'image'\n if (mimeType === 'application/pdf') return 'pdf'\n if (mimeType.startsWith('video/')) return 'video'\n if (mimeType.startsWith('audio/')) return 'audio'\n }\n\n const ext = url.split('?')[0].split('.').pop()?.toLowerCase()\n if (['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'avif', 'bmp'].includes(ext ?? '')) return 'image'\n if (ext === 'pdf') return 'pdf'\n if (['mp4', 'webm', 'ogg', 'mov'].includes(ext ?? '')) return 'video'\n if (['mp3', 'wav', 'ogg', 'aac', 'flac'].includes(ext ?? '')) return 'audio'\n\n if (url.includes('figma.com') || url.includes('loom.com') || url.includes('youtube.com') || url.includes('youtu.be') || url.includes('vimeo.com')) {\n return 'embed'\n }\n\n return 'image'\n}\n\nfunction getEmbedUrl(url: string): string {\n const ytMatch = url.match(/(?:youtube\\.com\\/watch\\?v=|youtu\\.be\\/)([^&?]+)/)\n if (ytMatch) return `https://www.youtube.com/embed/${ytMatch[1]}`\n\n const vimeoMatch = url.match(/vimeo\\.com\\/(\\d+)/)\n if (vimeoMatch) return `https://player.vimeo.com/video/${vimeoMatch[1]}`\n\n if (url.includes('figma.com')) return `https://www.figma.com/embed?embed_host=share&url=${encodeURIComponent(url)}`\n\n const loomMatch = url.match(/loom\\.com\\/share\\/([^?]+)/)\n if (loomMatch) return `https://www.loom.com/embed/${loomMatch[1]}`\n\n return url\n}\n\n// ============================================================\n// FilePreview — Main component (router)\n// ============================================================\n\nfunction FilePreview({\n url,\n type: typeProp,\n mimeType,\n alt,\n initialPage = 1,\n fileName,\n fileSize,\n onError,\n className,\n ...props\n}: FilePreviewProps) {\n const type = typeProp ?? detectType(url, mimeType)\n const fallback = <Skeleton className=\"h-64 w-full rounded-control\" />\n\n return (\n <div className={cn('flex flex-col gap-ds-03', className)} {...props}>\n {/* File info bar */}\n {(fileName || fileSize) && (\n <div className=\"flex items-center gap-ds-03\">\n {fileName && (\n <TruncatedText mode=\"middle\" className=\"min-w-0 text-ds-sm font-semibold text-surface-fg\">\n {fileName}\n </TruncatedText>\n )}\n {fileSize && <Badge variant=\"subtle\" size=\"xs\">{fileSize}</Badge>}\n </div>\n )}\n\n {/* Preview */}\n {type === 'image' && (\n <React.Suspense fallback={fallback}>\n <LazyImagePreview url={url} alt={alt} onError={onError} />\n </React.Suspense>\n )}\n {type === 'pdf' && (\n <React.Suspense fallback={fallback}>\n <LazyDocumentPreview url={url} initialPage={initialPage} onError={onError} />\n </React.Suspense>\n )}\n {type === 'video' && <VideoPreview url={url} onError={onError} />}\n {type === 'audio' && <AudioPreview url={url} fileName={fileName} onError={onError} />}\n {type === 'embed' && <EmbedPreview url={url} embedUrl={getEmbedUrl(url)} onError={onError} />}\n\n {/* Download — not shown for audio (it has its own layout) */}\n {type !== 'audio' && (\n <div className=\"flex justify-end\">\n <Button variant=\"ghost\" size=\"xs\" startIcon={<Icon icon={IconDownload} />} asChild>\n <a href={url} download target=\"_blank\" rel=\"noopener noreferrer\">Download</a>\n </Button>\n </div>\n )}\n </div>\n )\n}\n\nexport { FilePreview }\nexport type { FileType }\n"],"mappings":"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"}
@@ -83,7 +83,7 @@ function b({ label: i, value: a, onValueChange: o, options: s, className: c }) {
83
83
  }), u > 0 && /* @__PURE__ */ p(g.span, {
84
84
  initial: { scale: .8 },
85
85
  animate: { scale: 1 },
86
- transition: e.bouncy,
86
+ transition: e.snappy,
87
87
  className: "inline-flex",
88
88
  children: /* @__PURE__ */ p(r, {
89
89
  size: "xs",