@optilogic/core 1.0.0-beta.9 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/index.cjs +1115 -45
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +326 -1
  4. package/dist/index.d.ts +326 -1
  5. package/dist/index.js +1097 -46
  6. package/dist/index.js.map +1 -1
  7. package/dist/styles.css +22 -0
  8. package/dist/tailwind-preset.cjs +17 -2
  9. package/dist/tailwind-preset.cjs.map +1 -1
  10. package/dist/tailwind-preset.js +17 -2
  11. package/dist/tailwind-preset.js.map +1 -1
  12. package/package.json +15 -1
  13. package/src/components/autocomplete.tsx +2 -1
  14. package/src/components/button.tsx +10 -8
  15. package/src/components/calendar.tsx +7 -7
  16. package/src/components/data-grid/DataGrid.tsx +6 -1
  17. package/src/components/data-grid/components/CellEditor.tsx +3 -3
  18. package/src/components/data-grid/hooks/useDataGridState.ts +18 -3
  19. package/src/components/data-grid/types.ts +4 -0
  20. package/src/components/data-grid/utils/dataProcessing.ts +40 -11
  21. package/src/components/date-picker.tsx +2 -1
  22. package/src/components/dropdown-menu.tsx +1 -1
  23. package/src/components/file-view/FileView.tsx +147 -0
  24. package/src/components/file-view/components/CodeRenderer.tsx +97 -0
  25. package/src/components/file-view/components/CsvRenderer.tsx +127 -0
  26. package/src/components/file-view/components/HtmlRenderer.tsx +24 -0
  27. package/src/components/file-view/components/ImageRenderer.tsx +67 -0
  28. package/src/components/file-view/components/MarkdownRenderer.tsx +304 -0
  29. package/src/components/file-view/components/PlainTextRenderer.tsx +27 -0
  30. package/src/components/file-view/components/index.ts +4 -0
  31. package/src/components/file-view/hooks/index.ts +5 -0
  32. package/src/components/file-view/hooks/useContentType.ts +34 -0
  33. package/src/components/file-view/hooks/useDarkMode.ts +62 -0
  34. package/src/components/file-view/hooks/useHighlightedTokens.ts +83 -0
  35. package/src/components/file-view/hooks/useShikiHighlighter.ts +69 -0
  36. package/src/components/file-view/index.ts +47 -0
  37. package/src/components/file-view/types.ts +180 -0
  38. package/src/components/file-view/utils/contentTypeDetection.ts +157 -0
  39. package/src/components/file-view/utils/index.ts +12 -0
  40. package/src/components/file-view/utils/languageMapping.ts +78 -0
  41. package/src/components/file-view/utils/rendererRegistry.ts +42 -0
  42. package/src/components/input.tsx +1 -1
  43. package/src/components/popover.tsx +1 -1
  44. package/src/components/select.tsx +1 -1
  45. package/src/components/switch.tsx +5 -3
  46. package/src/components/textarea.tsx +1 -1
  47. package/src/index.ts +39 -0
  48. package/src/styles.css +22 -0
  49. package/src/tailwind-preset.ts +17 -1
  50. package/src/theme/index.ts +5 -0
  51. package/src/theme/presets.ts +112 -2
  52. package/src/theme/types.ts +35 -0
  53. package/src/theme/utils.ts +231 -0
package/dist/index.js CHANGED
@@ -22,23 +22,25 @@ import { Toaster as Toaster$1 } from 'sonner';
22
22
  import { useVirtualizer } from '@tanstack/react-virtual';
23
23
  import { format, parse, isValid, parseISO } from 'date-fns';
24
24
  import { DayPicker } from 'react-day-picker';
25
+ import ReactMarkdownImport from 'react-markdown';
26
+ import remarkGfmImport from 'remark-gfm';
25
27
 
26
28
  // src/utils/cn.ts
27
29
  function cn(...inputs) {
28
30
  return twMerge(clsx(inputs));
29
31
  }
30
32
  var buttonVariants = cva(
31
- "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
33
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
32
34
  {
33
35
  variants: {
34
36
  variant: {
35
- default: "bg-muted text-muted-foreground hover:bg-accent hover:text-accent-foreground",
36
- primary: "bg-accent text-accent-foreground shadow hover:shadow-md",
37
- destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90 hover:shadow-md",
38
- outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
39
- secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
40
- ghost: "hover:bg-accent hover:text-accent-foreground",
41
- link: "text-primary underline-offset-4 hover:underline"
37
+ default: "bg-muted text-foreground hover:bg-accent hover:text-accent-foreground disabled:opacity-50 disabled:text-muted-foreground",
38
+ primary: "bg-accent text-accent-foreground shadow hover:shadow-md disabled:opacity-50",
39
+ destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90 hover:shadow-md disabled:opacity-50",
40
+ outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground disabled:opacity-50 disabled:bg-muted/20 disabled:text-muted-foreground",
41
+ secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80 disabled:opacity-50",
42
+ ghost: "text-foreground hover:bg-accent hover:text-accent-foreground disabled:text-muted-foreground disabled:bg-muted/30 disabled:opacity-70",
43
+ link: "text-primary underline-offset-4 hover:underline disabled:opacity-50 disabled:text-muted-foreground"
42
44
  },
43
45
  size: {
44
46
  default: "h-9 px-4 py-2",
@@ -74,7 +76,7 @@ var Input = React20.forwardRef(
74
76
  {
75
77
  type,
76
78
  className: cn(
77
- "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
79
+ "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground hover:border-input-hover focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:border-input md:text-sm",
78
80
  className
79
81
  ),
80
82
  ref,
@@ -102,7 +104,7 @@ var Textarea = React20.forwardRef(
102
104
  "textarea",
103
105
  {
104
106
  className: cn(
105
- "flex min-h-[60px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
107
+ "flex min-h-[60px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-sm placeholder:text-muted-foreground hover:border-input-hover focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:border-input md:text-sm",
106
108
  className
107
109
  ),
108
110
  ref,
@@ -190,9 +192,11 @@ var Switch = React20.forwardRef(({ className, ...props }, ref) => /* @__PURE__ *
190
192
  // Focus styles
191
193
  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
192
194
  // Disabled styles
193
- "disabled:cursor-not-allowed disabled:opacity-50",
195
+ "disabled:cursor-not-allowed disabled:opacity-50 disabled:bg-muted",
194
196
  // Unchecked state
195
- "bg-input",
197
+ "bg-toggle-track",
198
+ // Hover
199
+ "hover:bg-toggle-track/80 data-[state=checked]:hover:bg-primary/80",
196
200
  // Checked state
197
201
  "data-[state=checked]:bg-primary",
198
202
  className
@@ -205,7 +209,7 @@ var Switch = React20.forwardRef(({ className, ...props }, ref) => /* @__PURE__ *
205
209
  className: cn(
206
210
  // Base styles
207
211
  "pointer-events-none block h-4 w-4 rounded-full",
208
- "bg-background shadow-lg ring-0",
212
+ "bg-toggle-track-foreground shadow-lg ring-0",
209
213
  "transition-transform",
210
214
  // Position based on state
211
215
  "data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0"
@@ -268,7 +272,7 @@ var SelectTrigger = React20.forwardRef(({ className, children, ...props }, ref)
268
272
  {
269
273
  ref,
270
274
  className: cn(
271
- "flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background data-[placeholder]:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
275
+ "flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background data-[placeholder]:text-muted-foreground hover:border-input-hover focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:border-input [&>span]:line-clamp-1",
272
276
  className
273
277
  ),
274
278
  ...props,
@@ -604,7 +608,7 @@ var PopoverContent = React20.forwardRef(({ className, align = "center", sideOffs
604
608
  align,
605
609
  sideOffset,
606
610
  className: cn(
607
- "z-50 w-72 rounded-md border border-border bg-popover p-4 text-popover-foreground shadow-md outline-none",
611
+ "z-50 w-auto max-w-[90vw] rounded-md border border-border bg-popover p-4 text-popover-foreground shadow-md outline-none",
608
612
  // Animation
609
613
  "data-[state=open]:animate-in data-[state=closed]:animate-out",
610
614
  "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
@@ -665,7 +669,7 @@ var DropdownMenuContent = React20.forwardRef(({ className, sideOffset = 4, ...pr
665
669
  ref,
666
670
  sideOffset,
667
671
  className: cn(
668
- "z-50 min-w-[8rem] overflow-hidden rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md",
672
+ "z-50 min-w-[8rem] max-w-[90vw] overflow-hidden rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md",
669
673
  "data-[state=open]:animate-in data-[state=closed]:animate-out",
670
674
  "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
671
675
  "data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
@@ -2702,19 +2706,19 @@ function Calendar({
2702
2706
  className: cn("p-3", className),
2703
2707
  classNames: {
2704
2708
  months: "flex flex-col sm:flex-row gap-4",
2705
- month: "flex flex-col gap-4",
2706
- month_caption: "flex justify-center pt-1 relative items-center h-7 px-10",
2709
+ month: "flex flex-col gap-2 relative px-8",
2710
+ month_caption: "flex justify-center pt-1 items-center h-7",
2707
2711
  caption_label: "text-sm font-medium hidden",
2708
2712
  nav: "flex items-center gap-1",
2709
2713
  button_previous: cn(
2710
2714
  buttonVariants({ variant: "outline" }),
2711
- "absolute left-1 h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100 z-10"
2715
+ "absolute left-1 top-1/2 -translate-y-1/2 h-6 w-6 bg-transparent p-0 opacity-50 hover:opacity-100 z-10"
2712
2716
  ),
2713
2717
  button_next: cn(
2714
2718
  buttonVariants({ variant: "outline" }),
2715
- "absolute right-1 h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100 z-10"
2719
+ "absolute right-1 top-1/2 -translate-y-1/2 h-6 w-6 bg-transparent p-0 opacity-50 hover:opacity-100 z-10"
2716
2720
  ),
2717
- month_grid: "w-full border-collapse px-1",
2721
+ month_grid: "border-collapse mx-auto",
2718
2722
  weekdays: "flex",
2719
2723
  weekday: "text-muted-foreground rounded-md w-9 font-normal text-[0.8rem]",
2720
2724
  week: "flex w-full mt-2",
@@ -2739,12 +2743,12 @@ function Calendar({
2739
2743
  components: {
2740
2744
  Chevron: ({ orientation }) => {
2741
2745
  const Icon2 = orientation === "left" ? ChevronLeft : ChevronRight;
2742
- return /* @__PURE__ */ jsx(Icon2, { className: "h-4 w-4" });
2746
+ return /* @__PURE__ */ jsx(Icon2, { className: "h-3.5 w-3.5" });
2743
2747
  },
2744
2748
  MonthCaption: ({ calendarMonth }) => {
2745
2749
  const month = calendarMonth.date.getMonth();
2746
2750
  const year = calendarMonth.date.getFullYear();
2747
- return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-1 px-10", children: [
2751
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-1", children: [
2748
2752
  /* @__PURE__ */ jsxs(
2749
2753
  Select,
2750
2754
  {
@@ -2980,8 +2984,9 @@ function DatePickerInput({
2980
2984
  {
2981
2985
  className: cn(
2982
2986
  "flex items-center rounded-md border border-input bg-background ring-offset-background",
2987
+ "hover:border-input-hover",
2983
2988
  "focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2",
2984
- disabled && "opacity-50 cursor-not-allowed",
2989
+ disabled && "opacity-50 cursor-not-allowed hover:border-input",
2985
2990
  sizeClasses[size]
2986
2991
  ),
2987
2992
  children: [
@@ -3613,7 +3618,7 @@ function CellEditor({
3613
3618
  onKeyDown: handleKeyDown,
3614
3619
  onBlur: handleBlur,
3615
3620
  className: cn(
3616
- "h-full w-full border-0 rounded-none focus:ring-2 focus:ring-primary text-sm px-2",
3621
+ "h-full w-full border-0 rounded-none bg-background focus:ring-0 text-sm px-2",
3617
3622
  validationError && "ring-2 ring-destructive focus:ring-destructive",
3618
3623
  className
3619
3624
  ),
@@ -3631,7 +3636,7 @@ function CellEditor({
3631
3636
  onKeyDown: handleKeyDown,
3632
3637
  onBlur: handleBlur,
3633
3638
  className: cn(
3634
- "h-full w-full border-0 rounded-none focus:ring-2 focus:ring-primary text-sm px-2",
3639
+ "h-full w-full border-0 rounded-none bg-background focus:ring-0 text-sm px-2",
3635
3640
  validationError && "ring-2 ring-destructive focus:ring-destructive",
3636
3641
  className
3637
3642
  ),
@@ -3701,7 +3706,7 @@ function CellEditor({
3701
3706
  {
3702
3707
  ref: selectRef,
3703
3708
  className: cn(
3704
- "h-full w-full border-0 rounded-none focus:ring-2 focus:ring-primary text-sm",
3709
+ "h-full w-full border-0 rounded-none bg-background focus:ring-0 text-sm",
3705
3710
  validationError && "ring-2 ring-destructive focus:ring-destructive",
3706
3711
  className
3707
3712
  ),
@@ -3846,6 +3851,7 @@ function useDataGridState(options) {
3846
3851
  const [internalColumnWidths, setInternalColumnWidths] = useState(defaultColumnWidths);
3847
3852
  const [internalFocusedCell, setInternalFocusedCell] = useState(null);
3848
3853
  const [editingCell, setEditingCell] = useState(null);
3854
+ const processedDataRef = useRef([]);
3849
3855
  const sorting = isControlled.sorting ? controlledSorting : internalSorting;
3850
3856
  const filters = isControlled.filters ? controlledFilters : internalFilters;
3851
3857
  const focusedCell = isControlled.focusedCell ? controlledFocusedCell : internalFocusedCell;
@@ -3966,7 +3972,8 @@ function useDataGridState(options) {
3966
3972
  }
3967
3973
  const column = columns.find((c) => c.key === columnKey);
3968
3974
  if (!column || !column.editable) return;
3969
- const row = data[rowIndex];
3975
+ const resolvedData = processedDataRef.current.length > 0 ? processedDataRef.current : data;
3976
+ const row = resolvedData[rowIndex];
3970
3977
  if (!row) return;
3971
3978
  const value = getCellValue2(row, column);
3972
3979
  setEditingCell({
@@ -3988,7 +3995,8 @@ function useDataGridState(options) {
3988
3995
  const value = valueOverride !== void 0 ? valueOverride : editingCell.value;
3989
3996
  const column = columns.find((c) => c.key === columnKey);
3990
3997
  if (column?.validator) {
3991
- const row = data[rowIndex];
3998
+ const resolvedData = processedDataRef.current.length > 0 ? processedDataRef.current : data;
3999
+ const row = resolvedData[rowIndex];
3992
4000
  const validationResult = column.validator(value, row);
3993
4001
  if (validationResult !== true && typeof validationResult === "string") {
3994
4002
  return;
@@ -4020,6 +4028,7 @@ function useDataGridState(options) {
4020
4028
  commitEdit,
4021
4029
  cancelEdit
4022
4030
  },
4031
+ processedDataRef,
4023
4032
  isControlled
4024
4033
  };
4025
4034
  }
@@ -4473,16 +4482,44 @@ function applySorting(data, sorting, columns) {
4473
4482
  if (aVal == null) return sort.direction === "asc" ? 1 : -1;
4474
4483
  if (bVal == null) return sort.direction === "asc" ? -1 : 1;
4475
4484
  let comparison = 0;
4476
- if (typeof aVal === "string" && typeof bVal === "string") {
4477
- comparison = aVal.localeCompare(bVal);
4478
- } else if (typeof aVal === "number" && typeof bVal === "number") {
4479
- comparison = aVal - bVal;
4480
- } else if (aVal instanceof Date && bVal instanceof Date) {
4481
- comparison = aVal.getTime() - bVal.getTime();
4482
- } else if (typeof aVal === "boolean" && typeof bVal === "boolean") {
4483
- comparison = aVal === bVal ? 0 : aVal ? 1 : -1;
4484
- } else {
4485
+ if (column.dataType === "number") {
4486
+ const aNum = Number(aVal);
4487
+ const bNum = Number(bVal);
4488
+ if (!isNaN(aNum) && !isNaN(bNum)) {
4489
+ comparison = aNum - bNum;
4490
+ } else {
4491
+ comparison = String(aVal).localeCompare(String(bVal));
4492
+ }
4493
+ } else if (column.dataType === "date") {
4494
+ const aDate = aVal instanceof Date ? aVal : new Date(aVal);
4495
+ const bDate = bVal instanceof Date ? bVal : new Date(bVal);
4496
+ const aTime = isNaN(aDate.getTime()) ? 0 : aDate.getTime();
4497
+ const bTime = isNaN(bDate.getTime()) ? 0 : bDate.getTime();
4498
+ comparison = aTime - bTime;
4499
+ } else if (column.dataType === "boolean") {
4500
+ const aBool = aVal === true || aVal === "true" || aVal === 1;
4501
+ const bBool = bVal === true || bVal === "true" || bVal === 1;
4502
+ comparison = aBool === bBool ? 0 : aBool ? 1 : -1;
4503
+ } else if (column.dataType === "string") {
4485
4504
  comparison = String(aVal).localeCompare(String(bVal));
4505
+ } else {
4506
+ if (typeof aVal === "number" && typeof bVal === "number") {
4507
+ comparison = aVal - bVal;
4508
+ } else if (aVal instanceof Date && bVal instanceof Date) {
4509
+ comparison = aVal.getTime() - bVal.getTime();
4510
+ } else if (typeof aVal === "boolean" && typeof bVal === "boolean") {
4511
+ comparison = aVal === bVal ? 0 : aVal ? 1 : -1;
4512
+ } else {
4513
+ const aStr = String(aVal);
4514
+ const bStr = String(bVal);
4515
+ const aNum = Number(aStr);
4516
+ const bNum = Number(bStr);
4517
+ if (!isNaN(aNum) && !isNaN(bNum) && aStr !== "" && bStr !== "") {
4518
+ comparison = aNum - bNum;
4519
+ } else {
4520
+ comparison = aStr.localeCompare(bStr);
4521
+ }
4522
+ }
4486
4523
  }
4487
4524
  return sort.direction === "asc" ? comparison : -comparison;
4488
4525
  });
@@ -4704,7 +4741,7 @@ function DataGrid({
4704
4741
  },
4705
4742
  []
4706
4743
  );
4707
- const { state, actions, isControlled } = useDataGridState({
4744
+ const { state, actions, processedDataRef, isControlled } = useDataGridState({
4708
4745
  sorting: controlledSorting,
4709
4746
  filters: controlledFilters,
4710
4747
  columnWidths: controlledColumnWidths,
@@ -4743,6 +4780,7 @@ function DataGrid({
4743
4780
  state.sorting,
4744
4781
  visibleColumns
4745
4782
  ]);
4783
+ processedDataRef.current = processedData;
4746
4784
  const { resizingColumn, getResizeProps } = useColumnResizeManager({
4747
4785
  columns: visibleColumns,
4748
4786
  columnWidths: state.columnWidths,
@@ -5078,6 +5116,7 @@ function DataGrid({
5078
5116
  "flex-shrink-0 px-3 py-2 text-sm overflow-hidden",
5079
5117
  showColumnBorders && "border-r border-border last:border-r-0",
5080
5118
  isFocused && !isEditingThisCell && "ring-2 ring-inset ring-primary",
5119
+ isEditingThisCell && "ring-2 ring-inset ring-primary bg-background",
5081
5120
  column.align === "center" && "text-center",
5082
5121
  column.align === "right" && "text-right"
5083
5122
  ),
@@ -5247,6 +5286,7 @@ function DataGrid({
5247
5286
  "flex-shrink-0 px-3 py-2 text-sm overflow-hidden",
5248
5287
  showColumnBorders && "border-r border-border last:border-r-0",
5249
5288
  isFocused && !isEditingThisCell && "ring-2 ring-inset ring-primary",
5289
+ isEditingThisCell && "ring-2 ring-inset ring-primary bg-background",
5250
5290
  column.align === "center" && "text-center",
5251
5291
  column.align === "right" && "text-right"
5252
5292
  ),
@@ -5502,8 +5542,9 @@ function Autocomplete({
5502
5542
  disabled,
5503
5543
  className: cn(
5504
5544
  "flex h-9 w-full items-center justify-between gap-2 whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background",
5545
+ "hover:border-input-hover",
5505
5546
  "focus:outline-none focus:ring-1 focus:ring-ring",
5506
- "disabled:cursor-not-allowed disabled:opacity-50",
5547
+ "disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:border-input",
5507
5548
  !selectedOption && "text-muted-foreground",
5508
5549
  className
5509
5550
  ),
@@ -5734,11 +5775,21 @@ var GREEN_THEME = {
5734
5775
  border: "#243630",
5735
5776
  input: "#243630",
5736
5777
  ring: "#6FCF97",
5778
+ toggleTrack: "#354840",
5779
+ toggleTrackForeground: "#E6F5EC",
5780
+ inputHover: "#6FCF97",
5737
5781
  chart1: "#6FCF97",
5738
5782
  chart2: "#8FE3B0",
5739
5783
  chart3: "#9DB8A8",
5740
5784
  chart4: "#2d4038",
5741
5785
  chart5: "#354840",
5786
+ chart6: "#4CAF50",
5787
+ chart7: "#81C784",
5788
+ chart8: "#F2C94C",
5789
+ chart9: "#EB5757",
5790
+ chart10: "#56CCF2",
5791
+ chart11: "#BB6BD9",
5792
+ chart12: "#2D9CDB",
5742
5793
  radius: "0.5rem"
5743
5794
  };
5744
5795
  var OPTILOGIC_LEGACY_THEME = {
@@ -5769,13 +5820,23 @@ var OPTILOGIC_LEGACY_THEME = {
5769
5820
  chip: "#B8C5F9",
5770
5821
  chipForeground: "#0C0A5A",
5771
5822
  border: "#D0D0D0",
5772
- input: "#FFFFFF",
5823
+ input: "#D0D0D0",
5773
5824
  ring: "#5766F2",
5825
+ toggleTrack: "#D0D0D0",
5826
+ toggleTrackForeground: "#FFFFFF",
5827
+ inputHover: "#5766F2",
5774
5828
  chart1: "#78D237",
5775
5829
  chart2: "#F5CF47",
5776
5830
  chart3: "#5766F2",
5777
5831
  chart4: "#44BD7E",
5778
5832
  chart5: "#929BEF",
5833
+ chart6: "#DB2828",
5834
+ chart7: "#E07B39",
5835
+ chart8: "#2BBBAD",
5836
+ chart9: "#A5673F",
5837
+ chart10: "#00B5AD",
5838
+ chart11: "#6435C9",
5839
+ chart12: "#E03997",
5779
5840
  radius: "0.5rem"
5780
5841
  };
5781
5842
  var FUTURISTIC_THEME = {
@@ -5808,11 +5869,21 @@ var FUTURISTIC_THEME = {
5808
5869
  border: "#1e293b",
5809
5870
  input: "#1e293b",
5810
5871
  ring: "#6366f1",
5872
+ toggleTrack: "#334155",
5873
+ toggleTrackForeground: "#e0e7ff",
5874
+ inputHover: "#6366f1",
5811
5875
  chart1: "#6366f1",
5812
5876
  chart2: "#8b5cf6",
5813
5877
  chart3: "#a855f7",
5814
5878
  chart4: "#ec4899",
5815
5879
  chart5: "#f43f5e",
5880
+ chart6: "#06b6d4",
5881
+ chart7: "#14b8a6",
5882
+ chart8: "#f97316",
5883
+ chart9: "#eab308",
5884
+ chart10: "#22c55e",
5885
+ chart11: "#0ea5e9",
5886
+ chart12: "#f43f5e",
5816
5887
  radius: "0.5rem"
5817
5888
  };
5818
5889
  var NATURE_THEME = {
@@ -5845,11 +5916,21 @@ var NATURE_THEME = {
5845
5916
  border: "#243824",
5846
5917
  input: "#243824",
5847
5918
  ring: "#4caf50",
5919
+ toggleTrack: "#3d5a3d",
5920
+ toggleTrackForeground: "#e8f5e9",
5921
+ inputHover: "#4caf50",
5848
5922
  chart1: "#4caf50",
5849
5923
  chart2: "#66bb6a",
5850
5924
  chart3: "#81c784",
5851
5925
  chart4: "#a5d6a7",
5852
5926
  chart5: "#c8e6c9",
5927
+ chart6: "#8bc34a",
5928
+ chart7: "#cddc39",
5929
+ chart8: "#ffb74d",
5930
+ chart9: "#4db6ac",
5931
+ chart10: "#7986cb",
5932
+ chart11: "#e57373",
5933
+ chart12: "#64b5f6",
5853
5934
  radius: "0.5rem"
5854
5935
  };
5855
5936
  var SCIFI_THEME = {
@@ -5882,11 +5963,21 @@ var SCIFI_THEME = {
5882
5963
  border: "#30363d",
5883
5964
  input: "#21262d",
5884
5965
  ring: "#00d9ff",
5966
+ toggleTrack: "#30363d",
5967
+ toggleTrackForeground: "#c9d1d9",
5968
+ inputHover: "#00d9ff",
5885
5969
  chart1: "#00d9ff",
5886
5970
  chart2: "#ff00ff",
5887
5971
  chart3: "#00ff88",
5888
5972
  chart4: "#ffd93d",
5889
5973
  chart5: "#58a6ff",
5974
+ chart6: "#ff6b6b",
5975
+ chart7: "#4ecdc4",
5976
+ chart8: "#a855f7",
5977
+ chart9: "#fb923c",
5978
+ chart10: "#38bdf8",
5979
+ chart11: "#f472b6",
5980
+ chart12: "#34d399",
5890
5981
  radius: "0.5rem"
5891
5982
  };
5892
5983
  var OCEAN_THEME = {
@@ -5919,11 +6010,21 @@ var OCEAN_THEME = {
5919
6010
  border: "#1e3a5f",
5920
6011
  input: "#1e3a5f",
5921
6012
  ring: "#2196f3",
6013
+ toggleTrack: "#264a6e",
6014
+ toggleTrackForeground: "#e3f2fd",
6015
+ inputHover: "#2196f3",
5922
6016
  chart1: "#2196f3",
5923
6017
  chart2: "#00bcd4",
5924
6018
  chart3: "#03a9f4",
5925
6019
  chart4: "#0288d1",
5926
6020
  chart5: "#0277bd",
6021
+ chart6: "#26c6da",
6022
+ chart7: "#42a5f5",
6023
+ chart8: "#66bb6a",
6024
+ chart9: "#ffb74d",
6025
+ chart10: "#ef5350",
6026
+ chart11: "#ab47bc",
6027
+ chart12: "#78909c",
5927
6028
  radius: "0.5rem"
5928
6029
  };
5929
6030
  var SUNSET_THEME = {
@@ -5956,11 +6057,21 @@ var SUNSET_THEME = {
5956
6057
  border: "#241424",
5957
6058
  input: "#241424",
5958
6059
  ring: "#ff6b35",
6060
+ toggleTrack: "#3d2a3d",
6061
+ toggleTrackForeground: "#ffe0e6",
6062
+ inputHover: "#ff6b35",
5959
6063
  chart1: "#ff6b35",
5960
6064
  chart2: "#c44569",
5961
6065
  chart3: "#f7931e",
5962
6066
  chart4: "#ffb703",
5963
6067
  chart5: "#ff8c42",
6068
+ chart6: "#06d6a0",
6069
+ chart7: "#118ab2",
6070
+ chart8: "#ef476f",
6071
+ chart9: "#ffd166",
6072
+ chart10: "#073b4c",
6073
+ chart11: "#8338ec",
6074
+ chart12: "#3a86ff",
5964
6075
  radius: "0.5rem"
5965
6076
  };
5966
6077
  var FOREST_THEME = {
@@ -5993,11 +6104,21 @@ var FOREST_THEME = {
5993
6104
  border: "#1b5e20",
5994
6105
  input: "#1a2e1a",
5995
6106
  ring: "#2e7d32",
6107
+ toggleTrack: "#2d4a2d",
6108
+ toggleTrackForeground: "#e8f5e9",
6109
+ inputHover: "#2e7d32",
5996
6110
  chart1: "#2e7d32",
5997
6111
  chart2: "#388e3c",
5998
6112
  chart3: "#43a047",
5999
6113
  chart4: "#66bb6a",
6000
6114
  chart5: "#81c784",
6115
+ chart6: "#a5d6a7",
6116
+ chart7: "#c8e6c9",
6117
+ chart8: "#ffb74d",
6118
+ chart9: "#4db6ac",
6119
+ chart10: "#7986cb",
6120
+ chart11: "#e57373",
6121
+ chart12: "#64b5f6",
6001
6122
  radius: "0.5rem"
6002
6123
  };
6003
6124
  var CYBERPUNK_THEME = {
@@ -6030,11 +6151,21 @@ var CYBERPUNK_THEME = {
6030
6151
  border: "#1a1a2e",
6031
6152
  input: "#16213e",
6032
6153
  ring: "#ff00ff",
6154
+ toggleTrack: "#2a2a4e",
6155
+ toggleTrackForeground: "#f0f0f0",
6156
+ inputHover: "#ff00ff",
6033
6157
  chart1: "#ff00ff",
6034
6158
  chart2: "#00ffff",
6035
6159
  chart3: "#00ff88",
6036
6160
  chart4: "#ffd700",
6037
6161
  chart5: "#ff1744",
6162
+ chart6: "#ff6b6b",
6163
+ chart7: "#4ecdc4",
6164
+ chart8: "#a855f7",
6165
+ chart9: "#fb923c",
6166
+ chart10: "#38bdf8",
6167
+ chart11: "#84cc16",
6168
+ chart12: "#f97316",
6038
6169
  radius: "0.5rem"
6039
6170
  };
6040
6171
  var MINIMALIST_LIGHT_THEME = {
@@ -6065,13 +6196,23 @@ var MINIMALIST_LIGHT_THEME = {
6065
6196
  chip: "#e9ecef",
6066
6197
  chipForeground: "#495057",
6067
6198
  border: "#dee2e6",
6068
- input: "#ffffff",
6199
+ input: "#dee2e6",
6069
6200
  ring: "#000000",
6201
+ toggleTrack: "#ced4da",
6202
+ toggleTrackForeground: "#ffffff",
6203
+ inputHover: "#6c757d",
6070
6204
  chart1: "#000000",
6071
6205
  chart2: "#6c757d",
6072
6206
  chart3: "#adb5bd",
6073
6207
  chart4: "#dee2e6",
6074
6208
  chart5: "#e9ecef",
6209
+ chart6: "#343a40",
6210
+ chart7: "#495057",
6211
+ chart8: "#868e96",
6212
+ chart9: "#5c6bc0",
6213
+ chart10: "#26a69a",
6214
+ chart11: "#ef5350",
6215
+ chart12: "#ffa726",
6075
6216
  radius: "0.5rem"
6076
6217
  };
6077
6218
  var DARK_ELEGANT_THEME = {
@@ -6104,11 +6245,21 @@ var DARK_ELEGANT_THEME = {
6104
6245
  border: "#2d2d2d",
6105
6246
  input: "#1e1e1e",
6106
6247
  ring: "#bb86fc",
6248
+ toggleTrack: "#3d3d3d",
6249
+ toggleTrackForeground: "#e0e0e0",
6250
+ inputHover: "#bb86fc",
6107
6251
  chart1: "#bb86fc",
6108
6252
  chart2: "#03dac6",
6109
6253
  chart3: "#4caf50",
6110
6254
  chart4: "#ff9800",
6111
6255
  chart5: "#cf6679",
6256
+ chart6: "#64b5f6",
6257
+ chart7: "#81c784",
6258
+ chart8: "#ffb74d",
6259
+ chart9: "#e57373",
6260
+ chart10: "#7986cb",
6261
+ chart11: "#4db6ac",
6262
+ chart12: "#f06292",
6112
6263
  radius: "0.5rem"
6113
6264
  };
6114
6265
  var PRESET_THEMES = [OPTILOGIC_LEGACY_THEME];
@@ -6194,13 +6345,32 @@ function themeToHsl(theme) {
6194
6345
  border: hexToHsl(theme.border),
6195
6346
  input: hexToHsl(theme.input),
6196
6347
  ring: hexToHsl(theme.ring),
6348
+ toggleTrack: theme.toggleTrack ? hexToHsl(theme.toggleTrack) : void 0,
6349
+ toggleTrackForeground: theme.toggleTrackForeground ? hexToHsl(theme.toggleTrackForeground) : void 0,
6350
+ inputHover: theme.inputHover ? hexToHsl(theme.inputHover) : void 0,
6197
6351
  chart1: hexToHsl(theme.chart1),
6198
6352
  chart2: hexToHsl(theme.chart2),
6199
6353
  chart3: hexToHsl(theme.chart3),
6200
6354
  chart4: hexToHsl(theme.chart4),
6201
- chart5: hexToHsl(theme.chart5)
6355
+ chart5: hexToHsl(theme.chart5),
6356
+ chart6: hexToHsl(theme.chart6),
6357
+ chart7: hexToHsl(theme.chart7),
6358
+ chart8: hexToHsl(theme.chart8),
6359
+ chart9: hexToHsl(theme.chart9),
6360
+ chart10: hexToHsl(theme.chart10),
6361
+ chart11: hexToHsl(theme.chart11),
6362
+ chart12: hexToHsl(theme.chart12)
6202
6363
  };
6203
6364
  }
6365
+ function deriveInputHoverHsl(hslTheme) {
6366
+ const parts = hslTheme.foreground.split(/\s+/);
6367
+ if (parts.length >= 3) {
6368
+ const h = parts[0];
6369
+ const s = parts[1];
6370
+ return `${h} ${s} 50%`;
6371
+ }
6372
+ return hslTheme.foreground;
6373
+ }
6204
6374
  function applyTheme(theme, targetElement) {
6205
6375
  const element = targetElement || document.documentElement;
6206
6376
  const hslTheme = themeToHsl(theme);
@@ -6235,11 +6405,33 @@ function applyTheme(theme, targetElement) {
6235
6405
  element.style.setProperty("--border", hslTheme.border);
6236
6406
  element.style.setProperty("--input", hslTheme.input);
6237
6407
  element.style.setProperty("--ring", hslTheme.ring);
6408
+ element.style.setProperty(
6409
+ "--toggle-track",
6410
+ hslTheme.toggleTrack ?? hslTheme.muted
6411
+ );
6412
+ element.style.setProperty(
6413
+ "--toggle-track-foreground",
6414
+ hslTheme.toggleTrackForeground ?? hslTheme.background
6415
+ );
6416
+ element.style.setProperty(
6417
+ "--input-hover",
6418
+ hslTheme.inputHover ?? deriveInputHoverHsl(hslTheme)
6419
+ );
6238
6420
  element.style.setProperty("--chart-1", hslTheme.chart1);
6239
6421
  element.style.setProperty("--chart-2", hslTheme.chart2);
6240
6422
  element.style.setProperty("--chart-3", hslTheme.chart3);
6241
6423
  element.style.setProperty("--chart-4", hslTheme.chart4);
6242
6424
  element.style.setProperty("--chart-5", hslTheme.chart5);
6425
+ element.style.setProperty("--chart-6", hslTheme.chart6);
6426
+ element.style.setProperty("--chart-7", hslTheme.chart7);
6427
+ element.style.setProperty("--chart-8", hslTheme.chart8);
6428
+ element.style.setProperty("--chart-9", hslTheme.chart9);
6429
+ element.style.setProperty("--chart-10", hslTheme.chart10);
6430
+ element.style.setProperty("--chart-11", hslTheme.chart11);
6431
+ element.style.setProperty("--chart-12", hslTheme.chart12);
6432
+ if (theme.disabledOpacity) {
6433
+ element.style.setProperty("--disabled-opacity", theme.disabledOpacity);
6434
+ }
6243
6435
  if (theme.radius) {
6244
6436
  element.style.setProperty("--radius", theme.radius);
6245
6437
  }
@@ -6282,7 +6474,14 @@ function validateTheme(theme) {
6282
6474
  "chart2",
6283
6475
  "chart3",
6284
6476
  "chart4",
6285
- "chart5"
6477
+ "chart5",
6478
+ "chart6",
6479
+ "chart7",
6480
+ "chart8",
6481
+ "chart9",
6482
+ "chart10",
6483
+ "chart11",
6484
+ "chart12"
6286
6485
  ];
6287
6486
  for (const field of requiredFields) {
6288
6487
  if (!(field in t)) {
@@ -6330,11 +6529,21 @@ function areThemesEqual(theme1, theme2) {
6330
6529
  "border",
6331
6530
  "input",
6332
6531
  "ring",
6532
+ "toggleTrack",
6533
+ "toggleTrackForeground",
6534
+ "inputHover",
6333
6535
  "chart1",
6334
6536
  "chart2",
6335
6537
  "chart3",
6336
6538
  "chart4",
6337
- "chart5"
6539
+ "chart5",
6540
+ "chart6",
6541
+ "chart7",
6542
+ "chart8",
6543
+ "chart9",
6544
+ "chart10",
6545
+ "chart11",
6546
+ "chart12"
6338
6547
  ];
6339
6548
  for (const field of colorFields) {
6340
6549
  if (theme1[field] !== theme2[field]) {
@@ -6768,6 +6977,848 @@ function useContextMenu() {
6768
6977
  };
6769
6978
  }
6770
6979
 
6771
- export { ALL_THEMES, Accordion, AccordionContent, AccordionItem, AccordionTrigger, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, Autocomplete, Badge, Board, BoardContent, BoardHeader, Button, CYBERPUNK_THEME, Calendar, Card, CardActions, CardContent, CardDescription, CardFooter, CardGrid, CardHeader, CardImage, CardList, CardTitle, CellEditor, Checkbox, Chip, ConfirmationModal, ContextMenu, CopyButton, DARK_ELEGANT_THEME, DataGrid, DataTable, DatePicker, DatePickerInput, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, FOREST_THEME, FUTURISTIC_THEME, FilterPopover, GREEN_THEME, HeaderCell, IconButton, Input, Label, LoadingSpinner, MINIMALIST_LIGHT_THEME, Modal, ModalButton, NATURE_THEME, OCEAN_THEME, OPTILOGIC_LEGACY_THEME, PRESET_THEMES, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, Progress, ResizablePanel, ResizeHandle, SCIFI_THEME, SUNSET_THEME, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, SelectableCard, Separator, Skeleton, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, ThemePicker, Toaster, Tooltip, TooltipArrow, TooltipContent, TooltipPortal, TooltipProvider, TooltipRoot, TooltipTrigger, accordionContentVariants, accordionItemVariants, accordionTriggerVariants, applyFilterOperator, applyFilters, applySorting, applyTheme, areThemesEqual, badgeVariants, boardVariants, buttonVariants, cardActionsVariants, cardGridVariants, cardImageVariants, cardListVariants, cardVariants, cloneTheme, cn, exportTheme, getCellValue, getCurrentTheme, getDefaultTheme, getPresetTheme, hexToHsl, iconButtonVariants, importTheme, isPresetTheme, labelVariants, loadingSpinnerVariants, themeToHsl, useColumnResize, useColumnResizeManager, useConfirmation, useContextMenu, useDataGridState, useKeyboardNavigation, validateTheme };
6980
+ // src/components/file-view/utils/contentTypeDetection.ts
6981
+ var EXTENSION_MAP = {
6982
+ // Code / programming languages
6983
+ ts: "code",
6984
+ tsx: "code",
6985
+ js: "code",
6986
+ jsx: "code",
6987
+ mjs: "code",
6988
+ cjs: "code",
6989
+ py: "code",
6990
+ rb: "code",
6991
+ go: "code",
6992
+ rs: "code",
6993
+ java: "code",
6994
+ c: "code",
6995
+ cpp: "code",
6996
+ h: "code",
6997
+ hpp: "code",
6998
+ cs: "code",
6999
+ php: "code",
7000
+ swift: "code",
7001
+ kt: "code",
7002
+ scala: "code",
7003
+ r: "code",
7004
+ sql: "code",
7005
+ sh: "code",
7006
+ bash: "code",
7007
+ zsh: "code",
7008
+ ps1: "code",
7009
+ bat: "code",
7010
+ lua: "code",
7011
+ perl: "code",
7012
+ pl: "code",
7013
+ // Config / data (code-rendered)
7014
+ json: "code",
7015
+ yaml: "code",
7016
+ yml: "code",
7017
+ toml: "code",
7018
+ xml: "code",
7019
+ html: "html",
7020
+ htm: "html",
7021
+ css: "code",
7022
+ scss: "code",
7023
+ sass: "code",
7024
+ less: "code",
7025
+ graphql: "code",
7026
+ gql: "code",
7027
+ // Optimization / domain-specific
7028
+ lp: "code",
7029
+ dat: "code",
7030
+ // Markdown
7031
+ md: "markdown",
7032
+ mdx: "markdown",
7033
+ // Images
7034
+ png: "image",
7035
+ jpg: "image",
7036
+ jpeg: "image",
7037
+ gif: "image",
7038
+ svg: "image",
7039
+ webp: "image",
7040
+ ico: "image",
7041
+ bmp: "image",
7042
+ // PDF
7043
+ pdf: "pdf",
7044
+ // CSV
7045
+ csv: "csv",
7046
+ tsv: "csv",
7047
+ // Plain text
7048
+ txt: "plaintext",
7049
+ log: "plaintext",
7050
+ env: "plaintext"
7051
+ };
7052
+ var SPECIAL_FILES = {
7053
+ dockerfile: "code",
7054
+ makefile: "code",
7055
+ rakefile: "code",
7056
+ gemfile: "code",
7057
+ procfile: "code"
7058
+ };
7059
+ function getFileExtension(fileName) {
7060
+ if (!fileName) return null;
7061
+ const baseName = fileName.split("/").pop() || fileName;
7062
+ if (baseName.startsWith(".") && !baseName.slice(1).includes(".")) {
7063
+ return baseName.slice(1).toLowerCase();
7064
+ }
7065
+ const lastDot = baseName.lastIndexOf(".");
7066
+ if (lastDot === -1 || lastDot === baseName.length - 1) return null;
7067
+ return baseName.slice(lastDot + 1).toLowerCase();
7068
+ }
7069
+ function detectContentType(fileName) {
7070
+ const baseName = (fileName.split("/").pop() || fileName).toLowerCase();
7071
+ const specialType = SPECIAL_FILES[baseName];
7072
+ if (specialType) {
7073
+ return { type: specialType, extension: baseName };
7074
+ }
7075
+ const extension = getFileExtension(fileName);
7076
+ if (!extension) {
7077
+ return { type: "unknown", extension: null };
7078
+ }
7079
+ const type = EXTENSION_MAP[extension] ?? "unknown";
7080
+ return { type, extension };
7081
+ }
7082
+ function isTextContentType(contentType) {
7083
+ return contentType === "code" || contentType === "markdown" || contentType === "plaintext" || contentType === "csv" || contentType === "html" || contentType === "unknown";
7084
+ }
7085
+ function isUrlContentType(contentType) {
7086
+ return contentType === "image" || contentType === "pdf";
7087
+ }
7088
+
7089
+ // src/components/file-view/hooks/useContentType.ts
7090
+ function useContentType({
7091
+ fileName,
7092
+ contentTypeOverride
7093
+ }) {
7094
+ return useMemo(() => {
7095
+ if (contentTypeOverride) {
7096
+ return {
7097
+ type: contentTypeOverride,
7098
+ extension: null,
7099
+ isOverridden: true
7100
+ };
7101
+ }
7102
+ const detected = detectContentType(fileName);
7103
+ return { ...detected, isOverridden: false };
7104
+ }, [fileName, contentTypeOverride]);
7105
+ }
7106
+ var globalHighlighter = null;
7107
+ var initPromise = null;
7108
+ async function getOrCreateHighlighter() {
7109
+ if (globalHighlighter) return globalHighlighter;
7110
+ if (!initPromise) {
7111
+ initPromise = (async () => {
7112
+ try {
7113
+ const { createHighlighter } = await import('shiki');
7114
+ globalHighlighter = await createHighlighter({
7115
+ themes: ["github-light", "github-dark"],
7116
+ langs: []
7117
+ });
7118
+ return globalHighlighter;
7119
+ } catch {
7120
+ return null;
7121
+ }
7122
+ })();
7123
+ }
7124
+ return initPromise;
7125
+ }
7126
+ function useShikiHighlighter() {
7127
+ const [state, setState] = useState({
7128
+ highlighter: globalHighlighter,
7129
+ isReady: globalHighlighter !== null
7130
+ });
7131
+ useEffect(() => {
7132
+ if (globalHighlighter) {
7133
+ setState({ highlighter: globalHighlighter, isReady: true });
7134
+ return;
7135
+ }
7136
+ let cancelled = false;
7137
+ getOrCreateHighlighter().then((h) => {
7138
+ if (!cancelled) {
7139
+ setState({ highlighter: h, isReady: true });
7140
+ }
7141
+ });
7142
+ return () => {
7143
+ cancelled = true;
7144
+ };
7145
+ }, []);
7146
+ return state;
7147
+ }
7148
+ function getBackgroundLightness(el) {
7149
+ const value = getComputedStyle(el).getPropertyValue("--background").trim();
7150
+ if (!value) return null;
7151
+ const match = value.match(/(\d+(?:\.\d+)?)%\s*$/);
7152
+ return match ? parseFloat(match[1]) : null;
7153
+ }
7154
+ function useDarkMode() {
7155
+ const checkDark = useCallback(() => {
7156
+ if (typeof document === "undefined") return false;
7157
+ const lightness = getBackgroundLightness(document.documentElement);
7158
+ if (lightness === null) {
7159
+ return document.documentElement.classList.contains("dark");
7160
+ }
7161
+ return lightness < 50;
7162
+ }, []);
7163
+ const [isDark, setIsDark] = useState(checkDark);
7164
+ useEffect(() => {
7165
+ const el = document.documentElement;
7166
+ const update = () => {
7167
+ setIsDark(checkDark());
7168
+ };
7169
+ const observer = new MutationObserver(update);
7170
+ observer.observe(el, {
7171
+ attributes: true,
7172
+ attributeFilter: ["class", "style"]
7173
+ });
7174
+ update();
7175
+ return () => observer.disconnect();
7176
+ }, [checkDark]);
7177
+ return isDark;
7178
+ }
7179
+
7180
+ // src/components/file-view/hooks/useHighlightedTokens.ts
7181
+ function useHighlightedTokens(code, language) {
7182
+ const { highlighter, isReady } = useShikiHighlighter();
7183
+ const isDark = useDarkMode();
7184
+ const [lines, setLines] = useState(null);
7185
+ useEffect(() => {
7186
+ if (!isReady || !highlighter || !code) {
7187
+ setLines(null);
7188
+ return;
7189
+ }
7190
+ let cancelled = false;
7191
+ const theme = isDark ? "github-dark" : "github-light";
7192
+ (async () => {
7193
+ try {
7194
+ if (language !== "text") {
7195
+ const loadedLangs = highlighter.getLoadedLanguages();
7196
+ if (!loadedLangs.includes(language)) {
7197
+ await highlighter.loadLanguage(language);
7198
+ }
7199
+ }
7200
+ } catch {
7201
+ if (!cancelled) setLines(null);
7202
+ return;
7203
+ }
7204
+ if (cancelled) return;
7205
+ try {
7206
+ const result = highlighter.codeToTokens(code, {
7207
+ lang: language,
7208
+ theme
7209
+ });
7210
+ if (!cancelled) {
7211
+ setLines(
7212
+ result.tokens.map(
7213
+ (line) => line.map((token) => ({
7214
+ content: token.content,
7215
+ color: token.color
7216
+ }))
7217
+ )
7218
+ );
7219
+ }
7220
+ } catch {
7221
+ if (!cancelled) setLines(null);
7222
+ }
7223
+ })();
7224
+ return () => {
7225
+ cancelled = true;
7226
+ };
7227
+ }, [highlighter, isReady, code, language, isDark]);
7228
+ return { lines };
7229
+ }
7230
+
7231
+ // src/components/file-view/utils/languageMapping.ts
7232
+ var EXTENSION_OVERRIDES = {
7233
+ yml: "yaml",
7234
+ htm: "html",
7235
+ cjs: "javascript",
7236
+ mjs: "javascript",
7237
+ jsx: "jsx",
7238
+ tsx: "tsx",
7239
+ h: "c",
7240
+ hpp: "cpp",
7241
+ cs: "csharp",
7242
+ rb: "ruby",
7243
+ sh: "shellscript",
7244
+ bash: "shellscript",
7245
+ zsh: "shellscript",
7246
+ ps1: "powershell",
7247
+ bat: "bat",
7248
+ kt: "kotlin",
7249
+ rs: "rust",
7250
+ gql: "graphql",
7251
+ pl: "perl",
7252
+ sass: "sass",
7253
+ scss: "scss",
7254
+ less: "less",
7255
+ // Domain-specific / data formats without shiki support
7256
+ lp: "text",
7257
+ dat: "text",
7258
+ env: "shellscript"
7259
+ };
7260
+ var SPECIAL_FILE_LANGUAGES = {
7261
+ dockerfile: "dockerfile",
7262
+ makefile: "makefile",
7263
+ rakefile: "ruby",
7264
+ gemfile: "ruby",
7265
+ procfile: "yaml"
7266
+ };
7267
+ function getLanguageFromFileName(fileName) {
7268
+ const baseName = (fileName.split("/").pop() || fileName).toLowerCase();
7269
+ const specialLang = SPECIAL_FILE_LANGUAGES[baseName];
7270
+ if (specialLang) return specialLang;
7271
+ let ext = null;
7272
+ if (baseName.startsWith(".") && !baseName.slice(1).includes(".")) {
7273
+ ext = baseName.slice(1);
7274
+ } else {
7275
+ const lastDot = baseName.lastIndexOf(".");
7276
+ if (lastDot !== -1 && lastDot !== baseName.length - 1) {
7277
+ ext = baseName.slice(lastDot + 1);
7278
+ }
7279
+ }
7280
+ if (!ext) return "text";
7281
+ return EXTENSION_OVERRIDES[ext] ?? ext;
7282
+ }
7283
+ function CodeRenderer({
7284
+ content,
7285
+ fileName,
7286
+ className
7287
+ }) {
7288
+ const language = React20.useMemo(
7289
+ () => getLanguageFromFileName(fileName),
7290
+ [fileName]
7291
+ );
7292
+ const plainLines = React20.useMemo(
7293
+ () => (content ?? "").split("\n"),
7294
+ [content]
7295
+ );
7296
+ const { lines: highlightedLines } = useHighlightedTokens(
7297
+ content ?? "",
7298
+ language
7299
+ );
7300
+ const gutterWidth = React20.useMemo(
7301
+ () => `${Math.max(String(plainLines.length).length, 2) + 2}ch`,
7302
+ [plainLines.length]
7303
+ );
7304
+ return /* @__PURE__ */ jsx(
7305
+ "div",
7306
+ {
7307
+ className: cn(
7308
+ "relative h-full w-full overflow-auto rounded-md border border-border bg-background",
7309
+ "scrollbar-thin",
7310
+ className
7311
+ ),
7312
+ children: /* @__PURE__ */ jsx("pre", { className: "m-0 p-0", children: /* @__PURE__ */ jsx("code", { className: "block font-mono text-sm leading-relaxed", children: highlightedLines ? highlightedLines.map((tokens, index) => /* @__PURE__ */ jsxs("div", { className: "flex", children: [
7313
+ /* @__PURE__ */ jsx(
7314
+ "span",
7315
+ {
7316
+ className: "sticky left-0 shrink-0 select-none border-r border-border bg-muted px-3 text-right text-muted-foreground",
7317
+ style: { minWidth: gutterWidth },
7318
+ children: index + 1
7319
+ }
7320
+ ),
7321
+ /* @__PURE__ */ jsx("span", { className: "whitespace-pre px-4", children: tokens.length === 0 || tokens.length === 1 && tokens[0].content === "" ? " " : tokens.map((token, ti) => /* @__PURE__ */ jsx("span", { style: { color: token.color }, children: token.content }, ti)) })
7322
+ ] }, index)) : plainLines.map((line, index) => /* @__PURE__ */ jsxs("div", { className: "flex", children: [
7323
+ /* @__PURE__ */ jsx(
7324
+ "span",
7325
+ {
7326
+ className: "sticky left-0 shrink-0 select-none border-r border-border bg-muted px-3 text-right text-muted-foreground",
7327
+ style: { minWidth: gutterWidth },
7328
+ children: index + 1
7329
+ }
7330
+ ),
7331
+ /* @__PURE__ */ jsx("span", { className: "whitespace-pre px-4 text-foreground", children: line || " " })
7332
+ ] }, index)) }) })
7333
+ }
7334
+ );
7335
+ }
7336
+ CodeRenderer.displayName = "CodeRenderer";
7337
+ function HighlightedCodeBlock({
7338
+ children,
7339
+ className: codeClassName,
7340
+ ...props
7341
+ }) {
7342
+ const language = React20.useMemo(() => {
7343
+ if (!codeClassName) return "text";
7344
+ const match = codeClassName.match(/language-(\S+)/);
7345
+ return match ? match[1] : "text";
7346
+ }, [codeClassName]);
7347
+ const code = React20.useMemo(() => {
7348
+ const raw = String(children);
7349
+ return raw.endsWith("\n") ? raw.slice(0, -1) : raw;
7350
+ }, [children]);
7351
+ const { lines } = useHighlightedTokens(code, language);
7352
+ if (lines) {
7353
+ return /* @__PURE__ */ jsx("code", { className: cn("bg-transparent p-0", codeClassName), ...props, children: lines.map((tokens, i) => /* @__PURE__ */ jsxs(React20.Fragment, { children: [
7354
+ i > 0 && "\n",
7355
+ tokens.map((token, ti) => /* @__PURE__ */ jsx("span", { style: { color: token.color }, children: token.content }, ti))
7356
+ ] }, i)) });
7357
+ }
7358
+ return /* @__PURE__ */ jsx("code", { className: cn("bg-transparent p-0", codeClassName), ...props, children });
7359
+ }
7360
+ function MarkdownImage({
7361
+ src,
7362
+ alt,
7363
+ resolveImageUrl,
7364
+ ...props
7365
+ }) {
7366
+ const needsResolution = !!src && !!resolveImageUrl && !src.startsWith("http://") && !src.startsWith("https://") && !src.startsWith("data:");
7367
+ const [resolvedUrl, setResolvedUrl] = React20.useState(null);
7368
+ const [error, setError] = React20.useState(false);
7369
+ const [loading, setLoading] = React20.useState(needsResolution);
7370
+ React20.useEffect(() => {
7371
+ if (!needsResolution || !src || !resolveImageUrl) return;
7372
+ let cancelled = false;
7373
+ let blobUrl = null;
7374
+ setLoading(true);
7375
+ setError(false);
7376
+ setResolvedUrl(null);
7377
+ Promise.resolve(resolveImageUrl(src)).then((url) => {
7378
+ if (cancelled) {
7379
+ if (url.startsWith("blob:")) URL.revokeObjectURL(url);
7380
+ return;
7381
+ }
7382
+ if (url.startsWith("blob:")) blobUrl = url;
7383
+ setResolvedUrl(url);
7384
+ setLoading(false);
7385
+ }).catch(() => {
7386
+ if (!cancelled) {
7387
+ setError(true);
7388
+ setLoading(false);
7389
+ }
7390
+ });
7391
+ return () => {
7392
+ cancelled = true;
7393
+ if (blobUrl) URL.revokeObjectURL(blobUrl);
7394
+ };
7395
+ }, [needsResolution, src, resolveImageUrl]);
7396
+ if (!needsResolution) {
7397
+ return /* @__PURE__ */ jsx("img", { src, alt, className: "max-w-full rounded", ...props });
7398
+ }
7399
+ if (loading) {
7400
+ return /* @__PURE__ */ jsx("span", { className: "my-2 inline-block h-24 w-40 animate-pulse rounded bg-muted" });
7401
+ }
7402
+ if (error || !resolvedUrl) {
7403
+ return /* @__PURE__ */ jsx("img", { src, alt, className: "max-w-full rounded", ...props });
7404
+ }
7405
+ return /* @__PURE__ */ jsx(
7406
+ "img",
7407
+ {
7408
+ src: resolvedUrl,
7409
+ alt,
7410
+ className: "max-w-full rounded",
7411
+ ...props
7412
+ }
7413
+ );
7414
+ }
7415
+ function MarkdownRenderer({
7416
+ content,
7417
+ className,
7418
+ resolveImageUrl
7419
+ }) {
7420
+ const markdownComponents = React20.useMemo(
7421
+ () => ({
7422
+ code: ({
7423
+ children,
7424
+ className: codeClassName,
7425
+ ...props
7426
+ }) => {
7427
+ const isInline = !codeClassName;
7428
+ if (isInline) {
7429
+ return /* @__PURE__ */ jsx(
7430
+ "code",
7431
+ {
7432
+ className: "rounded bg-muted px-1.5 py-0.5 font-mono text-[85%]",
7433
+ ...props,
7434
+ children
7435
+ }
7436
+ );
7437
+ }
7438
+ return /* @__PURE__ */ jsx(HighlightedCodeBlock, { className: codeClassName, ...props, children });
7439
+ },
7440
+ pre: ({ children, ...props }) => /* @__PURE__ */ jsx(
7441
+ "pre",
7442
+ {
7443
+ className: "my-4 overflow-auto rounded-md border border-border bg-muted p-4 font-mono text-sm leading-relaxed",
7444
+ ...props,
7445
+ children
7446
+ }
7447
+ ),
7448
+ blockquote: ({
7449
+ children,
7450
+ ...props
7451
+ }) => /* @__PURE__ */ jsx(
7452
+ "blockquote",
7453
+ {
7454
+ className: "my-4 border-l-4 border-border pl-4 text-muted-foreground",
7455
+ ...props,
7456
+ children
7457
+ }
7458
+ ),
7459
+ a: ({
7460
+ children,
7461
+ href,
7462
+ ...props
7463
+ }) => /* @__PURE__ */ jsx(
7464
+ "a",
7465
+ {
7466
+ href,
7467
+ target: "_blank",
7468
+ rel: "noopener noreferrer",
7469
+ className: "text-primary hover:underline",
7470
+ ...props,
7471
+ children
7472
+ }
7473
+ ),
7474
+ table: ({
7475
+ children,
7476
+ ...props
7477
+ }) => /* @__PURE__ */ jsx("div", { className: "my-4 overflow-auto", children: /* @__PURE__ */ jsx(
7478
+ "table",
7479
+ {
7480
+ className: "w-full border-collapse border border-border text-sm",
7481
+ ...props,
7482
+ children
7483
+ }
7484
+ ) }),
7485
+ th: ({
7486
+ children,
7487
+ ...props
7488
+ }) => /* @__PURE__ */ jsx(
7489
+ "th",
7490
+ {
7491
+ className: "border border-border bg-muted px-3 py-2 text-left font-semibold",
7492
+ ...props,
7493
+ children
7494
+ }
7495
+ ),
7496
+ td: ({
7497
+ children,
7498
+ ...props
7499
+ }) => /* @__PURE__ */ jsx("td", { className: "border border-border px-3 py-2", ...props, children }),
7500
+ img: ({
7501
+ src,
7502
+ alt,
7503
+ ...props
7504
+ }) => /* @__PURE__ */ jsx(
7505
+ MarkdownImage,
7506
+ {
7507
+ src,
7508
+ alt,
7509
+ resolveImageUrl,
7510
+ ...props
7511
+ }
7512
+ )
7513
+ }),
7514
+ [resolveImageUrl]
7515
+ );
7516
+ return /* @__PURE__ */ jsx(
7517
+ "div",
7518
+ {
7519
+ className: cn(
7520
+ "h-full w-full overflow-auto bg-background p-6 text-sm leading-relaxed text-foreground",
7521
+ // Heading styles
7522
+ "[&_h1]:mb-4 [&_h1]:mt-6 [&_h1]:border-b [&_h1]:border-border [&_h1]:pb-2 [&_h1]:text-2xl [&_h1]:font-bold",
7523
+ "[&_h2]:mb-4 [&_h2]:mt-6 [&_h2]:border-b [&_h2]:border-border [&_h2]:pb-2 [&_h2]:text-xl [&_h2]:font-semibold",
7524
+ "[&_h3]:mb-3 [&_h3]:mt-5 [&_h3]:text-lg [&_h3]:font-semibold",
7525
+ "[&_h4]:mb-3 [&_h4]:mt-4 [&_h4]:text-base [&_h4]:font-semibold",
7526
+ // Paragraph and list styles
7527
+ "[&_p]:mb-4 [&_p]:leading-relaxed",
7528
+ "[&_ul]:mb-4 [&_ul]:list-disc [&_ul]:pl-6",
7529
+ "[&_ol]:mb-4 [&_ol]:list-decimal [&_ol]:pl-6",
7530
+ "[&_li]:mb-1",
7531
+ // Horizontal rule
7532
+ "[&_hr]:my-6 [&_hr]:border-border",
7533
+ "scrollbar-thin",
7534
+ className
7535
+ ),
7536
+ children: /* @__PURE__ */ jsx(
7537
+ ReactMarkdownImport,
7538
+ {
7539
+ remarkPlugins: [remarkGfmImport],
7540
+ components: markdownComponents,
7541
+ children: content ?? ""
7542
+ }
7543
+ )
7544
+ }
7545
+ );
7546
+ }
7547
+ MarkdownRenderer.displayName = "MarkdownRenderer";
7548
+ function ImageRenderer({
7549
+ url,
7550
+ fileName,
7551
+ className
7552
+ }) {
7553
+ const [hasError, setHasError] = React20.useState(false);
7554
+ React20.useEffect(() => {
7555
+ setHasError(false);
7556
+ }, [url]);
7557
+ if (!url) {
7558
+ return /* @__PURE__ */ jsx(
7559
+ "div",
7560
+ {
7561
+ className: cn(
7562
+ "flex h-full w-full items-center justify-center text-sm text-muted-foreground",
7563
+ className
7564
+ ),
7565
+ children: "No image URL provided"
7566
+ }
7567
+ );
7568
+ }
7569
+ if (hasError) {
7570
+ return /* @__PURE__ */ jsxs(
7571
+ "div",
7572
+ {
7573
+ className: cn(
7574
+ "flex h-full w-full flex-col items-center justify-center gap-2 text-sm text-muted-foreground",
7575
+ className
7576
+ ),
7577
+ children: [
7578
+ /* @__PURE__ */ jsx("span", { children: "Failed to load image" }),
7579
+ /* @__PURE__ */ jsx("span", { className: "text-xs", children: fileName })
7580
+ ]
7581
+ }
7582
+ );
7583
+ }
7584
+ return /* @__PURE__ */ jsx(
7585
+ "div",
7586
+ {
7587
+ className: cn(
7588
+ "flex h-full w-full items-center justify-center overflow-auto bg-background p-4",
7589
+ "scrollbar-thin",
7590
+ className
7591
+ ),
7592
+ children: /* @__PURE__ */ jsx(
7593
+ "img",
7594
+ {
7595
+ src: url,
7596
+ alt: fileName,
7597
+ onError: () => setHasError(true),
7598
+ className: "max-h-full max-w-full object-contain"
7599
+ }
7600
+ )
7601
+ }
7602
+ );
7603
+ }
7604
+ ImageRenderer.displayName = "ImageRenderer";
7605
+ function PlainTextRenderer({ content, className }) {
7606
+ return /* @__PURE__ */ jsx(
7607
+ "div",
7608
+ {
7609
+ className: cn(
7610
+ "h-full w-full overflow-auto rounded-md border border-border bg-background p-4",
7611
+ "scrollbar-thin",
7612
+ className
7613
+ ),
7614
+ children: /* @__PURE__ */ jsx("pre", { className: "m-0 whitespace-pre-wrap break-words font-mono text-sm leading-relaxed text-foreground", children: content ?? "" })
7615
+ }
7616
+ );
7617
+ }
7618
+ PlainTextRenderer.displayName = "PlainTextRenderer";
7619
+ function parseCSV(text) {
7620
+ const lines = text.split("\n");
7621
+ if (lines.length === 0) return { headers: [], rows: [] };
7622
+ const firstLine = lines[0] ?? "";
7623
+ const delimiter = firstLine.includes(" ") ? " " : ",";
7624
+ function parseLine(line) {
7625
+ const fields = [];
7626
+ let current = "";
7627
+ let inQuotes = false;
7628
+ let i = 0;
7629
+ while (i < line.length) {
7630
+ const char = line[i];
7631
+ if (inQuotes) {
7632
+ if (char === '"') {
7633
+ if (i + 1 < line.length && line[i + 1] === '"') {
7634
+ current += '"';
7635
+ i += 2;
7636
+ } else {
7637
+ inQuotes = false;
7638
+ i++;
7639
+ }
7640
+ } else {
7641
+ current += char;
7642
+ i++;
7643
+ }
7644
+ } else {
7645
+ if (char === '"') {
7646
+ inQuotes = true;
7647
+ i++;
7648
+ } else if (char === delimiter) {
7649
+ fields.push(current.trim());
7650
+ current = "";
7651
+ i++;
7652
+ } else {
7653
+ current += char;
7654
+ i++;
7655
+ }
7656
+ }
7657
+ }
7658
+ fields.push(current.trim());
7659
+ return fields;
7660
+ }
7661
+ const headers = parseLine(firstLine);
7662
+ const rows = [];
7663
+ for (let i = 1; i < lines.length; i++) {
7664
+ const line = lines[i];
7665
+ if (line.trim() === "") continue;
7666
+ const values = parseLine(line);
7667
+ const row = {};
7668
+ for (let j = 0; j < headers.length; j++) {
7669
+ row[headers[j]] = values[j] ?? "";
7670
+ }
7671
+ rows.push(row);
7672
+ }
7673
+ return { headers, rows };
7674
+ }
7675
+ function CsvRenderer({ content, className }) {
7676
+ const { headers, rows } = React20.useMemo(
7677
+ () => parseCSV(content ?? ""),
7678
+ [content]
7679
+ );
7680
+ const columns = React20.useMemo(
7681
+ () => headers.map((header) => ({
7682
+ key: header,
7683
+ header,
7684
+ sortable: true,
7685
+ filterable: true,
7686
+ filterType: "text"
7687
+ })),
7688
+ [headers]
7689
+ );
7690
+ if (headers.length === 0) {
7691
+ return /* @__PURE__ */ jsx(
7692
+ "div",
7693
+ {
7694
+ className: cn(
7695
+ "flex h-full w-full items-center justify-center text-sm text-muted-foreground",
7696
+ className
7697
+ ),
7698
+ children: "No CSV data to display"
7699
+ }
7700
+ );
7701
+ }
7702
+ return /* @__PURE__ */ jsx("div", { className: cn("h-full w-full overflow-auto", className), children: /* @__PURE__ */ jsx(
7703
+ DataGrid,
7704
+ {
7705
+ data: rows,
7706
+ columns,
7707
+ getRowKey: (_, index) => String(index),
7708
+ resizableColumns: true
7709
+ }
7710
+ ) });
7711
+ }
7712
+ CsvRenderer.displayName = "CsvRenderer";
7713
+ function HtmlRenderer({
7714
+ content,
7715
+ fileName,
7716
+ className
7717
+ }) {
7718
+ return /* @__PURE__ */ jsx(
7719
+ "iframe",
7720
+ {
7721
+ srcDoc: content ?? "",
7722
+ sandbox: "allow-scripts",
7723
+ title: fileName,
7724
+ className: cn("h-full w-full border-0", className)
7725
+ }
7726
+ );
7727
+ }
7728
+ HtmlRenderer.displayName = "HtmlRenderer";
7729
+
7730
+ // src/components/file-view/utils/rendererRegistry.ts
7731
+ var DEFAULT_RENDERERS = {
7732
+ code: CodeRenderer,
7733
+ markdown: MarkdownRenderer,
7734
+ image: ImageRenderer,
7735
+ plaintext: PlainTextRenderer,
7736
+ csv: CsvRenderer,
7737
+ html: HtmlRenderer
7738
+ };
7739
+ function mergeRenderers(userRenderers) {
7740
+ if (!userRenderers) return DEFAULT_RENDERERS;
7741
+ return { ...DEFAULT_RENDERERS, ...userRenderers };
7742
+ }
7743
+ function resolveRenderer(registry, contentType) {
7744
+ return registry[contentType] ?? registry["plaintext"] ?? PlainTextRenderer;
7745
+ }
7746
+ function DefaultEmptyState({ message }) {
7747
+ return /* @__PURE__ */ jsx("div", { className: "flex h-full w-full items-center justify-center text-sm text-muted-foreground", children: message });
7748
+ }
7749
+ function DefaultLoadingState() {
7750
+ return /* @__PURE__ */ jsx("div", { className: "flex h-full w-full items-center justify-center", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-3", children: [
7751
+ /* @__PURE__ */ jsx("div", { className: "h-8 w-8 animate-spin rounded-full border-2 border-primary/20 border-t-primary" }),
7752
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Loading file..." })
7753
+ ] }) });
7754
+ }
7755
+ function DefaultErrorState({ error }) {
7756
+ return /* @__PURE__ */ jsx("div", { className: "flex h-full w-full items-center justify-center", children: /* @__PURE__ */ jsxs("div", { className: "flex max-w-sm flex-col items-center gap-3 text-center", children: [
7757
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-destructive", children: error.message }),
7758
+ error.onRetry && /* @__PURE__ */ jsx(
7759
+ "button",
7760
+ {
7761
+ onClick: error.onRetry,
7762
+ className: cn(
7763
+ "rounded-md border border-border bg-background px-3 py-1.5 text-sm text-foreground",
7764
+ "transition-colors hover:bg-muted"
7765
+ ),
7766
+ children: "Retry"
7767
+ }
7768
+ )
7769
+ ] }) });
7770
+ }
7771
+ function FileView({
7772
+ content,
7773
+ url = null,
7774
+ fileName,
7775
+ contentType: contentTypeOverride,
7776
+ renderers: userRenderers,
7777
+ loading = false,
7778
+ error = null,
7779
+ loadingComponent,
7780
+ emptyComponent,
7781
+ emptyMessage = "No content to display",
7782
+ errorComponent: ErrorComponent,
7783
+ className,
7784
+ rendererClassName,
7785
+ resolveImageUrl
7786
+ }) {
7787
+ const { type: resolvedType } = useContentType({
7788
+ fileName,
7789
+ contentTypeOverride
7790
+ });
7791
+ const registry = React20.useMemo(
7792
+ () => mergeRenderers(userRenderers),
7793
+ [userRenderers]
7794
+ );
7795
+ const Renderer = React20.useMemo(
7796
+ () => resolveRenderer(registry, resolvedType),
7797
+ [registry, resolvedType]
7798
+ );
7799
+ if (loading) {
7800
+ return /* @__PURE__ */ jsx("div", { className: cn("relative h-full w-full", className), children: loadingComponent ?? /* @__PURE__ */ jsx(DefaultLoadingState, {}) });
7801
+ }
7802
+ if (error) {
7803
+ return /* @__PURE__ */ jsx("div", { className: cn("relative h-full w-full", className), children: ErrorComponent ? /* @__PURE__ */ jsx(ErrorComponent, { error }) : /* @__PURE__ */ jsx(DefaultErrorState, { error }) });
7804
+ }
7805
+ if ((content == null || content === "") && url == null) {
7806
+ return /* @__PURE__ */ jsx("div", { className: cn("relative h-full w-full", className), children: emptyComponent ?? /* @__PURE__ */ jsx(DefaultEmptyState, { message: emptyMessage }) });
7807
+ }
7808
+ return /* @__PURE__ */ jsx("div", { className: cn("relative h-full w-full", className), children: /* @__PURE__ */ jsx(
7809
+ Renderer,
7810
+ {
7811
+ content,
7812
+ url: url ?? null,
7813
+ fileName,
7814
+ contentType: resolvedType,
7815
+ className: rendererClassName,
7816
+ resolveImageUrl
7817
+ }
7818
+ ) });
7819
+ }
7820
+ FileView.displayName = "FileView";
7821
+
7822
+ export { ALL_THEMES, Accordion, AccordionContent, AccordionItem, AccordionTrigger, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, Autocomplete, Badge, Board, BoardContent, BoardHeader, Button, CYBERPUNK_THEME, Calendar, Card, CardActions, CardContent, CardDescription, CardFooter, CardGrid, CardHeader, CardImage, CardList, CardTitle, CellEditor, Checkbox, Chip, CodeRenderer, ConfirmationModal, ContextMenu, CopyButton, CsvRenderer, DARK_ELEGANT_THEME, DEFAULT_RENDERERS, DataGrid, DataTable, DatePicker, DatePickerInput, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, FOREST_THEME, FUTURISTIC_THEME, FileView, FilterPopover, GREEN_THEME, HeaderCell, HtmlRenderer, IconButton, ImageRenderer, Input, Label, LoadingSpinner, MINIMALIST_LIGHT_THEME, MarkdownRenderer, Modal, ModalButton, NATURE_THEME, OCEAN_THEME, OPTILOGIC_LEGACY_THEME, PRESET_THEMES, PlainTextRenderer, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, Progress, ResizablePanel, ResizeHandle, SCIFI_THEME, SUNSET_THEME, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, SelectableCard, Separator, Skeleton, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, ThemePicker, Toaster, Tooltip, TooltipArrow, TooltipContent, TooltipPortal, TooltipProvider, TooltipRoot, TooltipTrigger, accordionContentVariants, accordionItemVariants, accordionTriggerVariants, applyFilterOperator, applyFilters, applySorting, applyTheme, areThemesEqual, badgeVariants, boardVariants, buttonVariants, cardActionsVariants, cardGridVariants, cardImageVariants, cardListVariants, cardVariants, cloneTheme, cn, detectContentType, exportTheme, getCellValue, getCurrentTheme, getDefaultTheme, getFileExtension, getPresetTheme, hexToHsl, iconButtonVariants, importTheme, isPresetTheme, isTextContentType, isUrlContentType, labelVariants, loadingSpinnerVariants, mergeRenderers, resolveRenderer, themeToHsl, useColumnResize, useColumnResizeManager, useConfirmation, useContentType, useContextMenu, useDataGridState, useKeyboardNavigation, validateTheme };
6772
7823
  //# sourceMappingURL=index.js.map
6773
7824
  //# sourceMappingURL=index.js.map