@optilogic/core 1.0.0-beta.8 → 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.cjs CHANGED
@@ -23,6 +23,10 @@ var sonner = require('sonner');
23
23
  var reactVirtual = require('@tanstack/react-virtual');
24
24
  var dateFns = require('date-fns');
25
25
  var reactDayPicker = require('react-day-picker');
26
+ var ReactMarkdownImport = require('react-markdown');
27
+ var remarkGfmImport = require('remark-gfm');
28
+
29
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
26
30
 
27
31
  function _interopNamespace(e) {
28
32
  if (e && e.__esModule) return e;
@@ -55,23 +59,25 @@ var TooltipPrimitive__namespace = /*#__PURE__*/_interopNamespace(TooltipPrimitiv
55
59
  var PopoverPrimitive__namespace = /*#__PURE__*/_interopNamespace(PopoverPrimitive);
56
60
  var DropdownMenuPrimitive__namespace = /*#__PURE__*/_interopNamespace(DropdownMenuPrimitive);
57
61
  var AlertDialogPrimitive__namespace = /*#__PURE__*/_interopNamespace(AlertDialogPrimitive);
62
+ var ReactMarkdownImport__default = /*#__PURE__*/_interopDefault(ReactMarkdownImport);
63
+ var remarkGfmImport__default = /*#__PURE__*/_interopDefault(remarkGfmImport);
58
64
 
59
65
  // src/utils/cn.ts
60
66
  function cn(...inputs) {
61
67
  return tailwindMerge.twMerge(clsx.clsx(inputs));
62
68
  }
63
69
  var buttonVariants = classVarianceAuthority.cva(
64
- "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",
70
+ "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",
65
71
  {
66
72
  variants: {
67
73
  variant: {
68
- default: "bg-muted text-muted-foreground hover:bg-accent hover:text-accent-foreground",
69
- primary: "bg-accent text-accent-foreground shadow hover:shadow-md",
70
- destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90 hover:shadow-md",
71
- outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
72
- secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
73
- ghost: "hover:bg-accent hover:text-accent-foreground",
74
- link: "text-primary underline-offset-4 hover:underline"
74
+ default: "bg-muted text-foreground hover:bg-accent hover:text-accent-foreground disabled:opacity-50 disabled:text-muted-foreground",
75
+ primary: "bg-accent text-accent-foreground shadow hover:shadow-md disabled:opacity-50",
76
+ destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90 hover:shadow-md disabled:opacity-50",
77
+ 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",
78
+ secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80 disabled:opacity-50",
79
+ ghost: "text-foreground hover:bg-accent hover:text-accent-foreground disabled:text-muted-foreground disabled:bg-muted/30 disabled:opacity-70",
80
+ link: "text-primary underline-offset-4 hover:underline disabled:opacity-50 disabled:text-muted-foreground"
75
81
  },
76
82
  size: {
77
83
  default: "h-9 px-4 py-2",
@@ -107,7 +113,7 @@ var Input = React20__namespace.forwardRef(
107
113
  {
108
114
  type,
109
115
  className: cn(
110
- "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",
116
+ "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",
111
117
  className
112
118
  ),
113
119
  ref,
@@ -135,7 +141,7 @@ var Textarea = React20__namespace.forwardRef(
135
141
  "textarea",
136
142
  {
137
143
  className: cn(
138
- "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",
144
+ "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",
139
145
  className
140
146
  ),
141
147
  ref,
@@ -223,9 +229,11 @@ var Switch = React20__namespace.forwardRef(({ className, ...props }, ref) => /*
223
229
  // Focus styles
224
230
  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
225
231
  // Disabled styles
226
- "disabled:cursor-not-allowed disabled:opacity-50",
232
+ "disabled:cursor-not-allowed disabled:opacity-50 disabled:bg-muted",
227
233
  // Unchecked state
228
- "bg-input",
234
+ "bg-toggle-track",
235
+ // Hover
236
+ "hover:bg-toggle-track/80 data-[state=checked]:hover:bg-primary/80",
229
237
  // Checked state
230
238
  "data-[state=checked]:bg-primary",
231
239
  className
@@ -238,7 +246,7 @@ var Switch = React20__namespace.forwardRef(({ className, ...props }, ref) => /*
238
246
  className: cn(
239
247
  // Base styles
240
248
  "pointer-events-none block h-4 w-4 rounded-full",
241
- "bg-background shadow-lg ring-0",
249
+ "bg-toggle-track-foreground shadow-lg ring-0",
242
250
  "transition-transform",
243
251
  // Position based on state
244
252
  "data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0"
@@ -301,7 +309,7 @@ var SelectTrigger = React20__namespace.forwardRef(({ className, children, ...pro
301
309
  {
302
310
  ref,
303
311
  className: cn(
304
- "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",
312
+ "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",
305
313
  className
306
314
  ),
307
315
  ...props,
@@ -637,7 +645,7 @@ var PopoverContent = React20__namespace.forwardRef(({ className, align = "center
637
645
  align,
638
646
  sideOffset,
639
647
  className: cn(
640
- "z-50 w-72 rounded-md border border-border bg-popover p-4 text-popover-foreground shadow-md outline-none",
648
+ "z-50 w-auto max-w-[90vw] rounded-md border border-border bg-popover p-4 text-popover-foreground shadow-md outline-none",
641
649
  // Animation
642
650
  "data-[state=open]:animate-in data-[state=closed]:animate-out",
643
651
  "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
@@ -698,7 +706,7 @@ var DropdownMenuContent = React20__namespace.forwardRef(({ className, sideOffset
698
706
  ref,
699
707
  sideOffset,
700
708
  className: cn(
701
- "z-50 min-w-[8rem] overflow-hidden rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md",
709
+ "z-50 min-w-[8rem] max-w-[90vw] overflow-hidden rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md",
702
710
  "data-[state=open]:animate-in data-[state=closed]:animate-out",
703
711
  "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
704
712
  "data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
@@ -2735,19 +2743,19 @@ function Calendar({
2735
2743
  className: cn("p-3", className),
2736
2744
  classNames: {
2737
2745
  months: "flex flex-col sm:flex-row gap-4",
2738
- month: "flex flex-col gap-4",
2739
- month_caption: "flex justify-center pt-1 relative items-center h-7 px-10",
2746
+ month: "flex flex-col gap-2 relative px-8",
2747
+ month_caption: "flex justify-center pt-1 items-center h-7",
2740
2748
  caption_label: "text-sm font-medium hidden",
2741
2749
  nav: "flex items-center gap-1",
2742
2750
  button_previous: cn(
2743
2751
  buttonVariants({ variant: "outline" }),
2744
- "absolute left-1 h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100 z-10"
2752
+ "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"
2745
2753
  ),
2746
2754
  button_next: cn(
2747
2755
  buttonVariants({ variant: "outline" }),
2748
- "absolute right-1 h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100 z-10"
2756
+ "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"
2749
2757
  ),
2750
- month_grid: "w-full border-collapse px-1",
2758
+ month_grid: "border-collapse mx-auto",
2751
2759
  weekdays: "flex",
2752
2760
  weekday: "text-muted-foreground rounded-md w-9 font-normal text-[0.8rem]",
2753
2761
  week: "flex w-full mt-2",
@@ -2772,12 +2780,12 @@ function Calendar({
2772
2780
  components: {
2773
2781
  Chevron: ({ orientation }) => {
2774
2782
  const Icon2 = orientation === "left" ? lucideReact.ChevronLeft : lucideReact.ChevronRight;
2775
- return /* @__PURE__ */ jsxRuntime.jsx(Icon2, { className: "h-4 w-4" });
2783
+ return /* @__PURE__ */ jsxRuntime.jsx(Icon2, { className: "h-3.5 w-3.5" });
2776
2784
  },
2777
2785
  MonthCaption: ({ calendarMonth }) => {
2778
2786
  const month = calendarMonth.date.getMonth();
2779
2787
  const year = calendarMonth.date.getFullYear();
2780
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center gap-1 px-10", children: [
2788
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center gap-1", children: [
2781
2789
  /* @__PURE__ */ jsxRuntime.jsxs(
2782
2790
  Select,
2783
2791
  {
@@ -3013,8 +3021,9 @@ function DatePickerInput({
3013
3021
  {
3014
3022
  className: cn(
3015
3023
  "flex items-center rounded-md border border-input bg-background ring-offset-background",
3024
+ "hover:border-input-hover",
3016
3025
  "focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2",
3017
- disabled && "opacity-50 cursor-not-allowed",
3026
+ disabled && "opacity-50 cursor-not-allowed hover:border-input",
3018
3027
  sizeClasses[size]
3019
3028
  ),
3020
3029
  children: [
@@ -3646,7 +3655,7 @@ function CellEditor({
3646
3655
  onKeyDown: handleKeyDown,
3647
3656
  onBlur: handleBlur,
3648
3657
  className: cn(
3649
- "h-full w-full border-0 rounded-none focus:ring-2 focus:ring-primary text-sm px-2",
3658
+ "h-full w-full border-0 rounded-none bg-background focus:ring-0 text-sm px-2",
3650
3659
  validationError && "ring-2 ring-destructive focus:ring-destructive",
3651
3660
  className
3652
3661
  ),
@@ -3664,7 +3673,7 @@ function CellEditor({
3664
3673
  onKeyDown: handleKeyDown,
3665
3674
  onBlur: handleBlur,
3666
3675
  className: cn(
3667
- "h-full w-full border-0 rounded-none focus:ring-2 focus:ring-primary text-sm px-2",
3676
+ "h-full w-full border-0 rounded-none bg-background focus:ring-0 text-sm px-2",
3668
3677
  validationError && "ring-2 ring-destructive focus:ring-destructive",
3669
3678
  className
3670
3679
  ),
@@ -3734,7 +3743,7 @@ function CellEditor({
3734
3743
  {
3735
3744
  ref: selectRef,
3736
3745
  className: cn(
3737
- "h-full w-full border-0 rounded-none focus:ring-2 focus:ring-primary text-sm",
3746
+ "h-full w-full border-0 rounded-none bg-background focus:ring-0 text-sm",
3738
3747
  validationError && "ring-2 ring-destructive focus:ring-destructive",
3739
3748
  className
3740
3749
  ),
@@ -3879,6 +3888,7 @@ function useDataGridState(options) {
3879
3888
  const [internalColumnWidths, setInternalColumnWidths] = React20.useState(defaultColumnWidths);
3880
3889
  const [internalFocusedCell, setInternalFocusedCell] = React20.useState(null);
3881
3890
  const [editingCell, setEditingCell] = React20.useState(null);
3891
+ const processedDataRef = React20.useRef([]);
3882
3892
  const sorting = isControlled.sorting ? controlledSorting : internalSorting;
3883
3893
  const filters = isControlled.filters ? controlledFilters : internalFilters;
3884
3894
  const focusedCell = isControlled.focusedCell ? controlledFocusedCell : internalFocusedCell;
@@ -3999,7 +4009,8 @@ function useDataGridState(options) {
3999
4009
  }
4000
4010
  const column = columns.find((c) => c.key === columnKey);
4001
4011
  if (!column || !column.editable) return;
4002
- const row = data[rowIndex];
4012
+ const resolvedData = processedDataRef.current.length > 0 ? processedDataRef.current : data;
4013
+ const row = resolvedData[rowIndex];
4003
4014
  if (!row) return;
4004
4015
  const value = getCellValue2(row, column);
4005
4016
  setEditingCell({
@@ -4021,7 +4032,8 @@ function useDataGridState(options) {
4021
4032
  const value = valueOverride !== void 0 ? valueOverride : editingCell.value;
4022
4033
  const column = columns.find((c) => c.key === columnKey);
4023
4034
  if (column?.validator) {
4024
- const row = data[rowIndex];
4035
+ const resolvedData = processedDataRef.current.length > 0 ? processedDataRef.current : data;
4036
+ const row = resolvedData[rowIndex];
4025
4037
  const validationResult = column.validator(value, row);
4026
4038
  if (validationResult !== true && typeof validationResult === "string") {
4027
4039
  return;
@@ -4053,6 +4065,7 @@ function useDataGridState(options) {
4053
4065
  commitEdit,
4054
4066
  cancelEdit
4055
4067
  },
4068
+ processedDataRef,
4056
4069
  isControlled
4057
4070
  };
4058
4071
  }
@@ -4506,16 +4519,44 @@ function applySorting(data, sorting, columns) {
4506
4519
  if (aVal == null) return sort.direction === "asc" ? 1 : -1;
4507
4520
  if (bVal == null) return sort.direction === "asc" ? -1 : 1;
4508
4521
  let comparison = 0;
4509
- if (typeof aVal === "string" && typeof bVal === "string") {
4510
- comparison = aVal.localeCompare(bVal);
4511
- } else if (typeof aVal === "number" && typeof bVal === "number") {
4512
- comparison = aVal - bVal;
4513
- } else if (aVal instanceof Date && bVal instanceof Date) {
4514
- comparison = aVal.getTime() - bVal.getTime();
4515
- } else if (typeof aVal === "boolean" && typeof bVal === "boolean") {
4516
- comparison = aVal === bVal ? 0 : aVal ? 1 : -1;
4517
- } else {
4522
+ if (column.dataType === "number") {
4523
+ const aNum = Number(aVal);
4524
+ const bNum = Number(bVal);
4525
+ if (!isNaN(aNum) && !isNaN(bNum)) {
4526
+ comparison = aNum - bNum;
4527
+ } else {
4528
+ comparison = String(aVal).localeCompare(String(bVal));
4529
+ }
4530
+ } else if (column.dataType === "date") {
4531
+ const aDate = aVal instanceof Date ? aVal : new Date(aVal);
4532
+ const bDate = bVal instanceof Date ? bVal : new Date(bVal);
4533
+ const aTime = isNaN(aDate.getTime()) ? 0 : aDate.getTime();
4534
+ const bTime = isNaN(bDate.getTime()) ? 0 : bDate.getTime();
4535
+ comparison = aTime - bTime;
4536
+ } else if (column.dataType === "boolean") {
4537
+ const aBool = aVal === true || aVal === "true" || aVal === 1;
4538
+ const bBool = bVal === true || bVal === "true" || bVal === 1;
4539
+ comparison = aBool === bBool ? 0 : aBool ? 1 : -1;
4540
+ } else if (column.dataType === "string") {
4518
4541
  comparison = String(aVal).localeCompare(String(bVal));
4542
+ } else {
4543
+ if (typeof aVal === "number" && typeof bVal === "number") {
4544
+ comparison = aVal - bVal;
4545
+ } else if (aVal instanceof Date && bVal instanceof Date) {
4546
+ comparison = aVal.getTime() - bVal.getTime();
4547
+ } else if (typeof aVal === "boolean" && typeof bVal === "boolean") {
4548
+ comparison = aVal === bVal ? 0 : aVal ? 1 : -1;
4549
+ } else {
4550
+ const aStr = String(aVal);
4551
+ const bStr = String(bVal);
4552
+ const aNum = Number(aStr);
4553
+ const bNum = Number(bStr);
4554
+ if (!isNaN(aNum) && !isNaN(bNum) && aStr !== "" && bStr !== "") {
4555
+ comparison = aNum - bNum;
4556
+ } else {
4557
+ comparison = aStr.localeCompare(bStr);
4558
+ }
4559
+ }
4519
4560
  }
4520
4561
  return sort.direction === "asc" ? comparison : -comparison;
4521
4562
  });
@@ -4737,7 +4778,7 @@ function DataGrid({
4737
4778
  },
4738
4779
  []
4739
4780
  );
4740
- const { state, actions, isControlled } = useDataGridState({
4781
+ const { state, actions, processedDataRef, isControlled } = useDataGridState({
4741
4782
  sorting: controlledSorting,
4742
4783
  filters: controlledFilters,
4743
4784
  columnWidths: controlledColumnWidths,
@@ -4776,6 +4817,7 @@ function DataGrid({
4776
4817
  state.sorting,
4777
4818
  visibleColumns
4778
4819
  ]);
4820
+ processedDataRef.current = processedData;
4779
4821
  const { resizingColumn, getResizeProps } = useColumnResizeManager({
4780
4822
  columns: visibleColumns,
4781
4823
  columnWidths: state.columnWidths,
@@ -5111,6 +5153,7 @@ function DataGrid({
5111
5153
  "flex-shrink-0 px-3 py-2 text-sm overflow-hidden",
5112
5154
  showColumnBorders && "border-r border-border last:border-r-0",
5113
5155
  isFocused && !isEditingThisCell && "ring-2 ring-inset ring-primary",
5156
+ isEditingThisCell && "ring-2 ring-inset ring-primary bg-background",
5114
5157
  column.align === "center" && "text-center",
5115
5158
  column.align === "right" && "text-right"
5116
5159
  ),
@@ -5280,6 +5323,7 @@ function DataGrid({
5280
5323
  "flex-shrink-0 px-3 py-2 text-sm overflow-hidden",
5281
5324
  showColumnBorders && "border-r border-border last:border-r-0",
5282
5325
  isFocused && !isEditingThisCell && "ring-2 ring-inset ring-primary",
5326
+ isEditingThisCell && "ring-2 ring-inset ring-primary bg-background",
5283
5327
  column.align === "center" && "text-center",
5284
5328
  column.align === "right" && "text-right"
5285
5329
  ),
@@ -5535,8 +5579,9 @@ function Autocomplete({
5535
5579
  disabled,
5536
5580
  className: cn(
5537
5581
  "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",
5582
+ "hover:border-input-hover",
5538
5583
  "focus:outline-none focus:ring-1 focus:ring-ring",
5539
- "disabled:cursor-not-allowed disabled:opacity-50",
5584
+ "disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:border-input",
5540
5585
  !selectedOption && "text-muted-foreground",
5541
5586
  className
5542
5587
  ),
@@ -5767,11 +5812,21 @@ var GREEN_THEME = {
5767
5812
  border: "#243630",
5768
5813
  input: "#243630",
5769
5814
  ring: "#6FCF97",
5815
+ toggleTrack: "#354840",
5816
+ toggleTrackForeground: "#E6F5EC",
5817
+ inputHover: "#6FCF97",
5770
5818
  chart1: "#6FCF97",
5771
5819
  chart2: "#8FE3B0",
5772
5820
  chart3: "#9DB8A8",
5773
5821
  chart4: "#2d4038",
5774
5822
  chart5: "#354840",
5823
+ chart6: "#4CAF50",
5824
+ chart7: "#81C784",
5825
+ chart8: "#F2C94C",
5826
+ chart9: "#EB5757",
5827
+ chart10: "#56CCF2",
5828
+ chart11: "#BB6BD9",
5829
+ chart12: "#2D9CDB",
5775
5830
  radius: "0.5rem"
5776
5831
  };
5777
5832
  var OPTILOGIC_LEGACY_THEME = {
@@ -5802,13 +5857,23 @@ var OPTILOGIC_LEGACY_THEME = {
5802
5857
  chip: "#B8C5F9",
5803
5858
  chipForeground: "#0C0A5A",
5804
5859
  border: "#D0D0D0",
5805
- input: "#FFFFFF",
5860
+ input: "#D0D0D0",
5806
5861
  ring: "#5766F2",
5862
+ toggleTrack: "#D0D0D0",
5863
+ toggleTrackForeground: "#FFFFFF",
5864
+ inputHover: "#5766F2",
5807
5865
  chart1: "#78D237",
5808
5866
  chart2: "#F5CF47",
5809
5867
  chart3: "#5766F2",
5810
5868
  chart4: "#44BD7E",
5811
5869
  chart5: "#929BEF",
5870
+ chart6: "#DB2828",
5871
+ chart7: "#E07B39",
5872
+ chart8: "#2BBBAD",
5873
+ chart9: "#A5673F",
5874
+ chart10: "#00B5AD",
5875
+ chart11: "#6435C9",
5876
+ chart12: "#E03997",
5812
5877
  radius: "0.5rem"
5813
5878
  };
5814
5879
  var FUTURISTIC_THEME = {
@@ -5841,11 +5906,21 @@ var FUTURISTIC_THEME = {
5841
5906
  border: "#1e293b",
5842
5907
  input: "#1e293b",
5843
5908
  ring: "#6366f1",
5909
+ toggleTrack: "#334155",
5910
+ toggleTrackForeground: "#e0e7ff",
5911
+ inputHover: "#6366f1",
5844
5912
  chart1: "#6366f1",
5845
5913
  chart2: "#8b5cf6",
5846
5914
  chart3: "#a855f7",
5847
5915
  chart4: "#ec4899",
5848
5916
  chart5: "#f43f5e",
5917
+ chart6: "#06b6d4",
5918
+ chart7: "#14b8a6",
5919
+ chart8: "#f97316",
5920
+ chart9: "#eab308",
5921
+ chart10: "#22c55e",
5922
+ chart11: "#0ea5e9",
5923
+ chart12: "#f43f5e",
5849
5924
  radius: "0.5rem"
5850
5925
  };
5851
5926
  var NATURE_THEME = {
@@ -5878,11 +5953,21 @@ var NATURE_THEME = {
5878
5953
  border: "#243824",
5879
5954
  input: "#243824",
5880
5955
  ring: "#4caf50",
5956
+ toggleTrack: "#3d5a3d",
5957
+ toggleTrackForeground: "#e8f5e9",
5958
+ inputHover: "#4caf50",
5881
5959
  chart1: "#4caf50",
5882
5960
  chart2: "#66bb6a",
5883
5961
  chart3: "#81c784",
5884
5962
  chart4: "#a5d6a7",
5885
5963
  chart5: "#c8e6c9",
5964
+ chart6: "#8bc34a",
5965
+ chart7: "#cddc39",
5966
+ chart8: "#ffb74d",
5967
+ chart9: "#4db6ac",
5968
+ chart10: "#7986cb",
5969
+ chart11: "#e57373",
5970
+ chart12: "#64b5f6",
5886
5971
  radius: "0.5rem"
5887
5972
  };
5888
5973
  var SCIFI_THEME = {
@@ -5915,11 +6000,21 @@ var SCIFI_THEME = {
5915
6000
  border: "#30363d",
5916
6001
  input: "#21262d",
5917
6002
  ring: "#00d9ff",
6003
+ toggleTrack: "#30363d",
6004
+ toggleTrackForeground: "#c9d1d9",
6005
+ inputHover: "#00d9ff",
5918
6006
  chart1: "#00d9ff",
5919
6007
  chart2: "#ff00ff",
5920
6008
  chart3: "#00ff88",
5921
6009
  chart4: "#ffd93d",
5922
6010
  chart5: "#58a6ff",
6011
+ chart6: "#ff6b6b",
6012
+ chart7: "#4ecdc4",
6013
+ chart8: "#a855f7",
6014
+ chart9: "#fb923c",
6015
+ chart10: "#38bdf8",
6016
+ chart11: "#f472b6",
6017
+ chart12: "#34d399",
5923
6018
  radius: "0.5rem"
5924
6019
  };
5925
6020
  var OCEAN_THEME = {
@@ -5952,11 +6047,21 @@ var OCEAN_THEME = {
5952
6047
  border: "#1e3a5f",
5953
6048
  input: "#1e3a5f",
5954
6049
  ring: "#2196f3",
6050
+ toggleTrack: "#264a6e",
6051
+ toggleTrackForeground: "#e3f2fd",
6052
+ inputHover: "#2196f3",
5955
6053
  chart1: "#2196f3",
5956
6054
  chart2: "#00bcd4",
5957
6055
  chart3: "#03a9f4",
5958
6056
  chart4: "#0288d1",
5959
6057
  chart5: "#0277bd",
6058
+ chart6: "#26c6da",
6059
+ chart7: "#42a5f5",
6060
+ chart8: "#66bb6a",
6061
+ chart9: "#ffb74d",
6062
+ chart10: "#ef5350",
6063
+ chart11: "#ab47bc",
6064
+ chart12: "#78909c",
5960
6065
  radius: "0.5rem"
5961
6066
  };
5962
6067
  var SUNSET_THEME = {
@@ -5989,11 +6094,21 @@ var SUNSET_THEME = {
5989
6094
  border: "#241424",
5990
6095
  input: "#241424",
5991
6096
  ring: "#ff6b35",
6097
+ toggleTrack: "#3d2a3d",
6098
+ toggleTrackForeground: "#ffe0e6",
6099
+ inputHover: "#ff6b35",
5992
6100
  chart1: "#ff6b35",
5993
6101
  chart2: "#c44569",
5994
6102
  chart3: "#f7931e",
5995
6103
  chart4: "#ffb703",
5996
6104
  chart5: "#ff8c42",
6105
+ chart6: "#06d6a0",
6106
+ chart7: "#118ab2",
6107
+ chart8: "#ef476f",
6108
+ chart9: "#ffd166",
6109
+ chart10: "#073b4c",
6110
+ chart11: "#8338ec",
6111
+ chart12: "#3a86ff",
5997
6112
  radius: "0.5rem"
5998
6113
  };
5999
6114
  var FOREST_THEME = {
@@ -6026,11 +6141,21 @@ var FOREST_THEME = {
6026
6141
  border: "#1b5e20",
6027
6142
  input: "#1a2e1a",
6028
6143
  ring: "#2e7d32",
6144
+ toggleTrack: "#2d4a2d",
6145
+ toggleTrackForeground: "#e8f5e9",
6146
+ inputHover: "#2e7d32",
6029
6147
  chart1: "#2e7d32",
6030
6148
  chart2: "#388e3c",
6031
6149
  chart3: "#43a047",
6032
6150
  chart4: "#66bb6a",
6033
6151
  chart5: "#81c784",
6152
+ chart6: "#a5d6a7",
6153
+ chart7: "#c8e6c9",
6154
+ chart8: "#ffb74d",
6155
+ chart9: "#4db6ac",
6156
+ chart10: "#7986cb",
6157
+ chart11: "#e57373",
6158
+ chart12: "#64b5f6",
6034
6159
  radius: "0.5rem"
6035
6160
  };
6036
6161
  var CYBERPUNK_THEME = {
@@ -6063,11 +6188,21 @@ var CYBERPUNK_THEME = {
6063
6188
  border: "#1a1a2e",
6064
6189
  input: "#16213e",
6065
6190
  ring: "#ff00ff",
6191
+ toggleTrack: "#2a2a4e",
6192
+ toggleTrackForeground: "#f0f0f0",
6193
+ inputHover: "#ff00ff",
6066
6194
  chart1: "#ff00ff",
6067
6195
  chart2: "#00ffff",
6068
6196
  chart3: "#00ff88",
6069
6197
  chart4: "#ffd700",
6070
6198
  chart5: "#ff1744",
6199
+ chart6: "#ff6b6b",
6200
+ chart7: "#4ecdc4",
6201
+ chart8: "#a855f7",
6202
+ chart9: "#fb923c",
6203
+ chart10: "#38bdf8",
6204
+ chart11: "#84cc16",
6205
+ chart12: "#f97316",
6071
6206
  radius: "0.5rem"
6072
6207
  };
6073
6208
  var MINIMALIST_LIGHT_THEME = {
@@ -6098,13 +6233,23 @@ var MINIMALIST_LIGHT_THEME = {
6098
6233
  chip: "#e9ecef",
6099
6234
  chipForeground: "#495057",
6100
6235
  border: "#dee2e6",
6101
- input: "#ffffff",
6236
+ input: "#dee2e6",
6102
6237
  ring: "#000000",
6238
+ toggleTrack: "#ced4da",
6239
+ toggleTrackForeground: "#ffffff",
6240
+ inputHover: "#6c757d",
6103
6241
  chart1: "#000000",
6104
6242
  chart2: "#6c757d",
6105
6243
  chart3: "#adb5bd",
6106
6244
  chart4: "#dee2e6",
6107
6245
  chart5: "#e9ecef",
6246
+ chart6: "#343a40",
6247
+ chart7: "#495057",
6248
+ chart8: "#868e96",
6249
+ chart9: "#5c6bc0",
6250
+ chart10: "#26a69a",
6251
+ chart11: "#ef5350",
6252
+ chart12: "#ffa726",
6108
6253
  radius: "0.5rem"
6109
6254
  };
6110
6255
  var DARK_ELEGANT_THEME = {
@@ -6137,11 +6282,21 @@ var DARK_ELEGANT_THEME = {
6137
6282
  border: "#2d2d2d",
6138
6283
  input: "#1e1e1e",
6139
6284
  ring: "#bb86fc",
6285
+ toggleTrack: "#3d3d3d",
6286
+ toggleTrackForeground: "#e0e0e0",
6287
+ inputHover: "#bb86fc",
6140
6288
  chart1: "#bb86fc",
6141
6289
  chart2: "#03dac6",
6142
6290
  chart3: "#4caf50",
6143
6291
  chart4: "#ff9800",
6144
6292
  chart5: "#cf6679",
6293
+ chart6: "#64b5f6",
6294
+ chart7: "#81c784",
6295
+ chart8: "#ffb74d",
6296
+ chart9: "#e57373",
6297
+ chart10: "#7986cb",
6298
+ chart11: "#4db6ac",
6299
+ chart12: "#f06292",
6145
6300
  radius: "0.5rem"
6146
6301
  };
6147
6302
  var PRESET_THEMES = [OPTILOGIC_LEGACY_THEME];
@@ -6227,13 +6382,32 @@ function themeToHsl(theme) {
6227
6382
  border: hexToHsl(theme.border),
6228
6383
  input: hexToHsl(theme.input),
6229
6384
  ring: hexToHsl(theme.ring),
6385
+ toggleTrack: theme.toggleTrack ? hexToHsl(theme.toggleTrack) : void 0,
6386
+ toggleTrackForeground: theme.toggleTrackForeground ? hexToHsl(theme.toggleTrackForeground) : void 0,
6387
+ inputHover: theme.inputHover ? hexToHsl(theme.inputHover) : void 0,
6230
6388
  chart1: hexToHsl(theme.chart1),
6231
6389
  chart2: hexToHsl(theme.chart2),
6232
6390
  chart3: hexToHsl(theme.chart3),
6233
6391
  chart4: hexToHsl(theme.chart4),
6234
- chart5: hexToHsl(theme.chart5)
6392
+ chart5: hexToHsl(theme.chart5),
6393
+ chart6: hexToHsl(theme.chart6),
6394
+ chart7: hexToHsl(theme.chart7),
6395
+ chart8: hexToHsl(theme.chart8),
6396
+ chart9: hexToHsl(theme.chart9),
6397
+ chart10: hexToHsl(theme.chart10),
6398
+ chart11: hexToHsl(theme.chart11),
6399
+ chart12: hexToHsl(theme.chart12)
6235
6400
  };
6236
6401
  }
6402
+ function deriveInputHoverHsl(hslTheme) {
6403
+ const parts = hslTheme.foreground.split(/\s+/);
6404
+ if (parts.length >= 3) {
6405
+ const h = parts[0];
6406
+ const s = parts[1];
6407
+ return `${h} ${s} 50%`;
6408
+ }
6409
+ return hslTheme.foreground;
6410
+ }
6237
6411
  function applyTheme(theme, targetElement) {
6238
6412
  const element = targetElement || document.documentElement;
6239
6413
  const hslTheme = themeToHsl(theme);
@@ -6268,11 +6442,33 @@ function applyTheme(theme, targetElement) {
6268
6442
  element.style.setProperty("--border", hslTheme.border);
6269
6443
  element.style.setProperty("--input", hslTheme.input);
6270
6444
  element.style.setProperty("--ring", hslTheme.ring);
6445
+ element.style.setProperty(
6446
+ "--toggle-track",
6447
+ hslTheme.toggleTrack ?? hslTheme.muted
6448
+ );
6449
+ element.style.setProperty(
6450
+ "--toggle-track-foreground",
6451
+ hslTheme.toggleTrackForeground ?? hslTheme.background
6452
+ );
6453
+ element.style.setProperty(
6454
+ "--input-hover",
6455
+ hslTheme.inputHover ?? deriveInputHoverHsl(hslTheme)
6456
+ );
6271
6457
  element.style.setProperty("--chart-1", hslTheme.chart1);
6272
6458
  element.style.setProperty("--chart-2", hslTheme.chart2);
6273
6459
  element.style.setProperty("--chart-3", hslTheme.chart3);
6274
6460
  element.style.setProperty("--chart-4", hslTheme.chart4);
6275
6461
  element.style.setProperty("--chart-5", hslTheme.chart5);
6462
+ element.style.setProperty("--chart-6", hslTheme.chart6);
6463
+ element.style.setProperty("--chart-7", hslTheme.chart7);
6464
+ element.style.setProperty("--chart-8", hslTheme.chart8);
6465
+ element.style.setProperty("--chart-9", hslTheme.chart9);
6466
+ element.style.setProperty("--chart-10", hslTheme.chart10);
6467
+ element.style.setProperty("--chart-11", hslTheme.chart11);
6468
+ element.style.setProperty("--chart-12", hslTheme.chart12);
6469
+ if (theme.disabledOpacity) {
6470
+ element.style.setProperty("--disabled-opacity", theme.disabledOpacity);
6471
+ }
6276
6472
  if (theme.radius) {
6277
6473
  element.style.setProperty("--radius", theme.radius);
6278
6474
  }
@@ -6315,7 +6511,14 @@ function validateTheme(theme) {
6315
6511
  "chart2",
6316
6512
  "chart3",
6317
6513
  "chart4",
6318
- "chart5"
6514
+ "chart5",
6515
+ "chart6",
6516
+ "chart7",
6517
+ "chart8",
6518
+ "chart9",
6519
+ "chart10",
6520
+ "chart11",
6521
+ "chart12"
6319
6522
  ];
6320
6523
  for (const field of requiredFields) {
6321
6524
  if (!(field in t)) {
@@ -6363,11 +6566,21 @@ function areThemesEqual(theme1, theme2) {
6363
6566
  "border",
6364
6567
  "input",
6365
6568
  "ring",
6569
+ "toggleTrack",
6570
+ "toggleTrackForeground",
6571
+ "inputHover",
6366
6572
  "chart1",
6367
6573
  "chart2",
6368
6574
  "chart3",
6369
6575
  "chart4",
6370
- "chart5"
6576
+ "chart5",
6577
+ "chart6",
6578
+ "chart7",
6579
+ "chart8",
6580
+ "chart9",
6581
+ "chart10",
6582
+ "chart11",
6583
+ "chart12"
6371
6584
  ];
6372
6585
  for (const field of colorFields) {
6373
6586
  if (theme1[field] !== theme2[field]) {
@@ -6801,6 +7014,848 @@ function useContextMenu() {
6801
7014
  };
6802
7015
  }
6803
7016
 
7017
+ // src/components/file-view/utils/contentTypeDetection.ts
7018
+ var EXTENSION_MAP = {
7019
+ // Code / programming languages
7020
+ ts: "code",
7021
+ tsx: "code",
7022
+ js: "code",
7023
+ jsx: "code",
7024
+ mjs: "code",
7025
+ cjs: "code",
7026
+ py: "code",
7027
+ rb: "code",
7028
+ go: "code",
7029
+ rs: "code",
7030
+ java: "code",
7031
+ c: "code",
7032
+ cpp: "code",
7033
+ h: "code",
7034
+ hpp: "code",
7035
+ cs: "code",
7036
+ php: "code",
7037
+ swift: "code",
7038
+ kt: "code",
7039
+ scala: "code",
7040
+ r: "code",
7041
+ sql: "code",
7042
+ sh: "code",
7043
+ bash: "code",
7044
+ zsh: "code",
7045
+ ps1: "code",
7046
+ bat: "code",
7047
+ lua: "code",
7048
+ perl: "code",
7049
+ pl: "code",
7050
+ // Config / data (code-rendered)
7051
+ json: "code",
7052
+ yaml: "code",
7053
+ yml: "code",
7054
+ toml: "code",
7055
+ xml: "code",
7056
+ html: "html",
7057
+ htm: "html",
7058
+ css: "code",
7059
+ scss: "code",
7060
+ sass: "code",
7061
+ less: "code",
7062
+ graphql: "code",
7063
+ gql: "code",
7064
+ // Optimization / domain-specific
7065
+ lp: "code",
7066
+ dat: "code",
7067
+ // Markdown
7068
+ md: "markdown",
7069
+ mdx: "markdown",
7070
+ // Images
7071
+ png: "image",
7072
+ jpg: "image",
7073
+ jpeg: "image",
7074
+ gif: "image",
7075
+ svg: "image",
7076
+ webp: "image",
7077
+ ico: "image",
7078
+ bmp: "image",
7079
+ // PDF
7080
+ pdf: "pdf",
7081
+ // CSV
7082
+ csv: "csv",
7083
+ tsv: "csv",
7084
+ // Plain text
7085
+ txt: "plaintext",
7086
+ log: "plaintext",
7087
+ env: "plaintext"
7088
+ };
7089
+ var SPECIAL_FILES = {
7090
+ dockerfile: "code",
7091
+ makefile: "code",
7092
+ rakefile: "code",
7093
+ gemfile: "code",
7094
+ procfile: "code"
7095
+ };
7096
+ function getFileExtension(fileName) {
7097
+ if (!fileName) return null;
7098
+ const baseName = fileName.split("/").pop() || fileName;
7099
+ if (baseName.startsWith(".") && !baseName.slice(1).includes(".")) {
7100
+ return baseName.slice(1).toLowerCase();
7101
+ }
7102
+ const lastDot = baseName.lastIndexOf(".");
7103
+ if (lastDot === -1 || lastDot === baseName.length - 1) return null;
7104
+ return baseName.slice(lastDot + 1).toLowerCase();
7105
+ }
7106
+ function detectContentType(fileName) {
7107
+ const baseName = (fileName.split("/").pop() || fileName).toLowerCase();
7108
+ const specialType = SPECIAL_FILES[baseName];
7109
+ if (specialType) {
7110
+ return { type: specialType, extension: baseName };
7111
+ }
7112
+ const extension = getFileExtension(fileName);
7113
+ if (!extension) {
7114
+ return { type: "unknown", extension: null };
7115
+ }
7116
+ const type = EXTENSION_MAP[extension] ?? "unknown";
7117
+ return { type, extension };
7118
+ }
7119
+ function isTextContentType(contentType) {
7120
+ return contentType === "code" || contentType === "markdown" || contentType === "plaintext" || contentType === "csv" || contentType === "html" || contentType === "unknown";
7121
+ }
7122
+ function isUrlContentType(contentType) {
7123
+ return contentType === "image" || contentType === "pdf";
7124
+ }
7125
+
7126
+ // src/components/file-view/hooks/useContentType.ts
7127
+ function useContentType({
7128
+ fileName,
7129
+ contentTypeOverride
7130
+ }) {
7131
+ return React20.useMemo(() => {
7132
+ if (contentTypeOverride) {
7133
+ return {
7134
+ type: contentTypeOverride,
7135
+ extension: null,
7136
+ isOverridden: true
7137
+ };
7138
+ }
7139
+ const detected = detectContentType(fileName);
7140
+ return { ...detected, isOverridden: false };
7141
+ }, [fileName, contentTypeOverride]);
7142
+ }
7143
+ var globalHighlighter = null;
7144
+ var initPromise = null;
7145
+ async function getOrCreateHighlighter() {
7146
+ if (globalHighlighter) return globalHighlighter;
7147
+ if (!initPromise) {
7148
+ initPromise = (async () => {
7149
+ try {
7150
+ const { createHighlighter } = await import('shiki');
7151
+ globalHighlighter = await createHighlighter({
7152
+ themes: ["github-light", "github-dark"],
7153
+ langs: []
7154
+ });
7155
+ return globalHighlighter;
7156
+ } catch {
7157
+ return null;
7158
+ }
7159
+ })();
7160
+ }
7161
+ return initPromise;
7162
+ }
7163
+ function useShikiHighlighter() {
7164
+ const [state, setState] = React20.useState({
7165
+ highlighter: globalHighlighter,
7166
+ isReady: globalHighlighter !== null
7167
+ });
7168
+ React20.useEffect(() => {
7169
+ if (globalHighlighter) {
7170
+ setState({ highlighter: globalHighlighter, isReady: true });
7171
+ return;
7172
+ }
7173
+ let cancelled = false;
7174
+ getOrCreateHighlighter().then((h) => {
7175
+ if (!cancelled) {
7176
+ setState({ highlighter: h, isReady: true });
7177
+ }
7178
+ });
7179
+ return () => {
7180
+ cancelled = true;
7181
+ };
7182
+ }, []);
7183
+ return state;
7184
+ }
7185
+ function getBackgroundLightness(el) {
7186
+ const value = getComputedStyle(el).getPropertyValue("--background").trim();
7187
+ if (!value) return null;
7188
+ const match = value.match(/(\d+(?:\.\d+)?)%\s*$/);
7189
+ return match ? parseFloat(match[1]) : null;
7190
+ }
7191
+ function useDarkMode() {
7192
+ const checkDark = React20.useCallback(() => {
7193
+ if (typeof document === "undefined") return false;
7194
+ const lightness = getBackgroundLightness(document.documentElement);
7195
+ if (lightness === null) {
7196
+ return document.documentElement.classList.contains("dark");
7197
+ }
7198
+ return lightness < 50;
7199
+ }, []);
7200
+ const [isDark, setIsDark] = React20.useState(checkDark);
7201
+ React20.useEffect(() => {
7202
+ const el = document.documentElement;
7203
+ const update = () => {
7204
+ setIsDark(checkDark());
7205
+ };
7206
+ const observer = new MutationObserver(update);
7207
+ observer.observe(el, {
7208
+ attributes: true,
7209
+ attributeFilter: ["class", "style"]
7210
+ });
7211
+ update();
7212
+ return () => observer.disconnect();
7213
+ }, [checkDark]);
7214
+ return isDark;
7215
+ }
7216
+
7217
+ // src/components/file-view/hooks/useHighlightedTokens.ts
7218
+ function useHighlightedTokens(code, language) {
7219
+ const { highlighter, isReady } = useShikiHighlighter();
7220
+ const isDark = useDarkMode();
7221
+ const [lines, setLines] = React20.useState(null);
7222
+ React20.useEffect(() => {
7223
+ if (!isReady || !highlighter || !code) {
7224
+ setLines(null);
7225
+ return;
7226
+ }
7227
+ let cancelled = false;
7228
+ const theme = isDark ? "github-dark" : "github-light";
7229
+ (async () => {
7230
+ try {
7231
+ if (language !== "text") {
7232
+ const loadedLangs = highlighter.getLoadedLanguages();
7233
+ if (!loadedLangs.includes(language)) {
7234
+ await highlighter.loadLanguage(language);
7235
+ }
7236
+ }
7237
+ } catch {
7238
+ if (!cancelled) setLines(null);
7239
+ return;
7240
+ }
7241
+ if (cancelled) return;
7242
+ try {
7243
+ const result = highlighter.codeToTokens(code, {
7244
+ lang: language,
7245
+ theme
7246
+ });
7247
+ if (!cancelled) {
7248
+ setLines(
7249
+ result.tokens.map(
7250
+ (line) => line.map((token) => ({
7251
+ content: token.content,
7252
+ color: token.color
7253
+ }))
7254
+ )
7255
+ );
7256
+ }
7257
+ } catch {
7258
+ if (!cancelled) setLines(null);
7259
+ }
7260
+ })();
7261
+ return () => {
7262
+ cancelled = true;
7263
+ };
7264
+ }, [highlighter, isReady, code, language, isDark]);
7265
+ return { lines };
7266
+ }
7267
+
7268
+ // src/components/file-view/utils/languageMapping.ts
7269
+ var EXTENSION_OVERRIDES = {
7270
+ yml: "yaml",
7271
+ htm: "html",
7272
+ cjs: "javascript",
7273
+ mjs: "javascript",
7274
+ jsx: "jsx",
7275
+ tsx: "tsx",
7276
+ h: "c",
7277
+ hpp: "cpp",
7278
+ cs: "csharp",
7279
+ rb: "ruby",
7280
+ sh: "shellscript",
7281
+ bash: "shellscript",
7282
+ zsh: "shellscript",
7283
+ ps1: "powershell",
7284
+ bat: "bat",
7285
+ kt: "kotlin",
7286
+ rs: "rust",
7287
+ gql: "graphql",
7288
+ pl: "perl",
7289
+ sass: "sass",
7290
+ scss: "scss",
7291
+ less: "less",
7292
+ // Domain-specific / data formats without shiki support
7293
+ lp: "text",
7294
+ dat: "text",
7295
+ env: "shellscript"
7296
+ };
7297
+ var SPECIAL_FILE_LANGUAGES = {
7298
+ dockerfile: "dockerfile",
7299
+ makefile: "makefile",
7300
+ rakefile: "ruby",
7301
+ gemfile: "ruby",
7302
+ procfile: "yaml"
7303
+ };
7304
+ function getLanguageFromFileName(fileName) {
7305
+ const baseName = (fileName.split("/").pop() || fileName).toLowerCase();
7306
+ const specialLang = SPECIAL_FILE_LANGUAGES[baseName];
7307
+ if (specialLang) return specialLang;
7308
+ let ext = null;
7309
+ if (baseName.startsWith(".") && !baseName.slice(1).includes(".")) {
7310
+ ext = baseName.slice(1);
7311
+ } else {
7312
+ const lastDot = baseName.lastIndexOf(".");
7313
+ if (lastDot !== -1 && lastDot !== baseName.length - 1) {
7314
+ ext = baseName.slice(lastDot + 1);
7315
+ }
7316
+ }
7317
+ if (!ext) return "text";
7318
+ return EXTENSION_OVERRIDES[ext] ?? ext;
7319
+ }
7320
+ function CodeRenderer({
7321
+ content,
7322
+ fileName,
7323
+ className
7324
+ }) {
7325
+ const language = React20__namespace.useMemo(
7326
+ () => getLanguageFromFileName(fileName),
7327
+ [fileName]
7328
+ );
7329
+ const plainLines = React20__namespace.useMemo(
7330
+ () => (content ?? "").split("\n"),
7331
+ [content]
7332
+ );
7333
+ const { lines: highlightedLines } = useHighlightedTokens(
7334
+ content ?? "",
7335
+ language
7336
+ );
7337
+ const gutterWidth = React20__namespace.useMemo(
7338
+ () => `${Math.max(String(plainLines.length).length, 2) + 2}ch`,
7339
+ [plainLines.length]
7340
+ );
7341
+ return /* @__PURE__ */ jsxRuntime.jsx(
7342
+ "div",
7343
+ {
7344
+ className: cn(
7345
+ "relative h-full w-full overflow-auto rounded-md border border-border bg-background",
7346
+ "scrollbar-thin",
7347
+ className
7348
+ ),
7349
+ children: /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "m-0 p-0", children: /* @__PURE__ */ jsxRuntime.jsx("code", { className: "block font-mono text-sm leading-relaxed", children: highlightedLines ? highlightedLines.map((tokens, index) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex", children: [
7350
+ /* @__PURE__ */ jsxRuntime.jsx(
7351
+ "span",
7352
+ {
7353
+ className: "sticky left-0 shrink-0 select-none border-r border-border bg-muted px-3 text-right text-muted-foreground",
7354
+ style: { minWidth: gutterWidth },
7355
+ children: index + 1
7356
+ }
7357
+ ),
7358
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "whitespace-pre px-4", children: tokens.length === 0 || tokens.length === 1 && tokens[0].content === "" ? " " : tokens.map((token, ti) => /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: token.color }, children: token.content }, ti)) })
7359
+ ] }, index)) : plainLines.map((line, index) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex", children: [
7360
+ /* @__PURE__ */ jsxRuntime.jsx(
7361
+ "span",
7362
+ {
7363
+ className: "sticky left-0 shrink-0 select-none border-r border-border bg-muted px-3 text-right text-muted-foreground",
7364
+ style: { minWidth: gutterWidth },
7365
+ children: index + 1
7366
+ }
7367
+ ),
7368
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "whitespace-pre px-4 text-foreground", children: line || " " })
7369
+ ] }, index)) }) })
7370
+ }
7371
+ );
7372
+ }
7373
+ CodeRenderer.displayName = "CodeRenderer";
7374
+ function HighlightedCodeBlock({
7375
+ children,
7376
+ className: codeClassName,
7377
+ ...props
7378
+ }) {
7379
+ const language = React20__namespace.useMemo(() => {
7380
+ if (!codeClassName) return "text";
7381
+ const match = codeClassName.match(/language-(\S+)/);
7382
+ return match ? match[1] : "text";
7383
+ }, [codeClassName]);
7384
+ const code = React20__namespace.useMemo(() => {
7385
+ const raw = String(children);
7386
+ return raw.endsWith("\n") ? raw.slice(0, -1) : raw;
7387
+ }, [children]);
7388
+ const { lines } = useHighlightedTokens(code, language);
7389
+ if (lines) {
7390
+ return /* @__PURE__ */ jsxRuntime.jsx("code", { className: cn("bg-transparent p-0", codeClassName), ...props, children: lines.map((tokens, i) => /* @__PURE__ */ jsxRuntime.jsxs(React20__namespace.Fragment, { children: [
7391
+ i > 0 && "\n",
7392
+ tokens.map((token, ti) => /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: token.color }, children: token.content }, ti))
7393
+ ] }, i)) });
7394
+ }
7395
+ return /* @__PURE__ */ jsxRuntime.jsx("code", { className: cn("bg-transparent p-0", codeClassName), ...props, children });
7396
+ }
7397
+ function MarkdownImage({
7398
+ src,
7399
+ alt,
7400
+ resolveImageUrl,
7401
+ ...props
7402
+ }) {
7403
+ const needsResolution = !!src && !!resolveImageUrl && !src.startsWith("http://") && !src.startsWith("https://") && !src.startsWith("data:");
7404
+ const [resolvedUrl, setResolvedUrl] = React20__namespace.useState(null);
7405
+ const [error, setError] = React20__namespace.useState(false);
7406
+ const [loading, setLoading] = React20__namespace.useState(needsResolution);
7407
+ React20__namespace.useEffect(() => {
7408
+ if (!needsResolution || !src || !resolveImageUrl) return;
7409
+ let cancelled = false;
7410
+ let blobUrl = null;
7411
+ setLoading(true);
7412
+ setError(false);
7413
+ setResolvedUrl(null);
7414
+ Promise.resolve(resolveImageUrl(src)).then((url) => {
7415
+ if (cancelled) {
7416
+ if (url.startsWith("blob:")) URL.revokeObjectURL(url);
7417
+ return;
7418
+ }
7419
+ if (url.startsWith("blob:")) blobUrl = url;
7420
+ setResolvedUrl(url);
7421
+ setLoading(false);
7422
+ }).catch(() => {
7423
+ if (!cancelled) {
7424
+ setError(true);
7425
+ setLoading(false);
7426
+ }
7427
+ });
7428
+ return () => {
7429
+ cancelled = true;
7430
+ if (blobUrl) URL.revokeObjectURL(blobUrl);
7431
+ };
7432
+ }, [needsResolution, src, resolveImageUrl]);
7433
+ if (!needsResolution) {
7434
+ return /* @__PURE__ */ jsxRuntime.jsx("img", { src, alt, className: "max-w-full rounded", ...props });
7435
+ }
7436
+ if (loading) {
7437
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "my-2 inline-block h-24 w-40 animate-pulse rounded bg-muted" });
7438
+ }
7439
+ if (error || !resolvedUrl) {
7440
+ return /* @__PURE__ */ jsxRuntime.jsx("img", { src, alt, className: "max-w-full rounded", ...props });
7441
+ }
7442
+ return /* @__PURE__ */ jsxRuntime.jsx(
7443
+ "img",
7444
+ {
7445
+ src: resolvedUrl,
7446
+ alt,
7447
+ className: "max-w-full rounded",
7448
+ ...props
7449
+ }
7450
+ );
7451
+ }
7452
+ function MarkdownRenderer({
7453
+ content,
7454
+ className,
7455
+ resolveImageUrl
7456
+ }) {
7457
+ const markdownComponents = React20__namespace.useMemo(
7458
+ () => ({
7459
+ code: ({
7460
+ children,
7461
+ className: codeClassName,
7462
+ ...props
7463
+ }) => {
7464
+ const isInline = !codeClassName;
7465
+ if (isInline) {
7466
+ return /* @__PURE__ */ jsxRuntime.jsx(
7467
+ "code",
7468
+ {
7469
+ className: "rounded bg-muted px-1.5 py-0.5 font-mono text-[85%]",
7470
+ ...props,
7471
+ children
7472
+ }
7473
+ );
7474
+ }
7475
+ return /* @__PURE__ */ jsxRuntime.jsx(HighlightedCodeBlock, { className: codeClassName, ...props, children });
7476
+ },
7477
+ pre: ({ children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx(
7478
+ "pre",
7479
+ {
7480
+ className: "my-4 overflow-auto rounded-md border border-border bg-muted p-4 font-mono text-sm leading-relaxed",
7481
+ ...props,
7482
+ children
7483
+ }
7484
+ ),
7485
+ blockquote: ({
7486
+ children,
7487
+ ...props
7488
+ }) => /* @__PURE__ */ jsxRuntime.jsx(
7489
+ "blockquote",
7490
+ {
7491
+ className: "my-4 border-l-4 border-border pl-4 text-muted-foreground",
7492
+ ...props,
7493
+ children
7494
+ }
7495
+ ),
7496
+ a: ({
7497
+ children,
7498
+ href,
7499
+ ...props
7500
+ }) => /* @__PURE__ */ jsxRuntime.jsx(
7501
+ "a",
7502
+ {
7503
+ href,
7504
+ target: "_blank",
7505
+ rel: "noopener noreferrer",
7506
+ className: "text-primary hover:underline",
7507
+ ...props,
7508
+ children
7509
+ }
7510
+ ),
7511
+ table: ({
7512
+ children,
7513
+ ...props
7514
+ }) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "my-4 overflow-auto", children: /* @__PURE__ */ jsxRuntime.jsx(
7515
+ "table",
7516
+ {
7517
+ className: "w-full border-collapse border border-border text-sm",
7518
+ ...props,
7519
+ children
7520
+ }
7521
+ ) }),
7522
+ th: ({
7523
+ children,
7524
+ ...props
7525
+ }) => /* @__PURE__ */ jsxRuntime.jsx(
7526
+ "th",
7527
+ {
7528
+ className: "border border-border bg-muted px-3 py-2 text-left font-semibold",
7529
+ ...props,
7530
+ children
7531
+ }
7532
+ ),
7533
+ td: ({
7534
+ children,
7535
+ ...props
7536
+ }) => /* @__PURE__ */ jsxRuntime.jsx("td", { className: "border border-border px-3 py-2", ...props, children }),
7537
+ img: ({
7538
+ src,
7539
+ alt,
7540
+ ...props
7541
+ }) => /* @__PURE__ */ jsxRuntime.jsx(
7542
+ MarkdownImage,
7543
+ {
7544
+ src,
7545
+ alt,
7546
+ resolveImageUrl,
7547
+ ...props
7548
+ }
7549
+ )
7550
+ }),
7551
+ [resolveImageUrl]
7552
+ );
7553
+ return /* @__PURE__ */ jsxRuntime.jsx(
7554
+ "div",
7555
+ {
7556
+ className: cn(
7557
+ "h-full w-full overflow-auto bg-background p-6 text-sm leading-relaxed text-foreground",
7558
+ // Heading styles
7559
+ "[&_h1]:mb-4 [&_h1]:mt-6 [&_h1]:border-b [&_h1]:border-border [&_h1]:pb-2 [&_h1]:text-2xl [&_h1]:font-bold",
7560
+ "[&_h2]:mb-4 [&_h2]:mt-6 [&_h2]:border-b [&_h2]:border-border [&_h2]:pb-2 [&_h2]:text-xl [&_h2]:font-semibold",
7561
+ "[&_h3]:mb-3 [&_h3]:mt-5 [&_h3]:text-lg [&_h3]:font-semibold",
7562
+ "[&_h4]:mb-3 [&_h4]:mt-4 [&_h4]:text-base [&_h4]:font-semibold",
7563
+ // Paragraph and list styles
7564
+ "[&_p]:mb-4 [&_p]:leading-relaxed",
7565
+ "[&_ul]:mb-4 [&_ul]:list-disc [&_ul]:pl-6",
7566
+ "[&_ol]:mb-4 [&_ol]:list-decimal [&_ol]:pl-6",
7567
+ "[&_li]:mb-1",
7568
+ // Horizontal rule
7569
+ "[&_hr]:my-6 [&_hr]:border-border",
7570
+ "scrollbar-thin",
7571
+ className
7572
+ ),
7573
+ children: /* @__PURE__ */ jsxRuntime.jsx(
7574
+ ReactMarkdownImport__default.default,
7575
+ {
7576
+ remarkPlugins: [remarkGfmImport__default.default],
7577
+ components: markdownComponents,
7578
+ children: content ?? ""
7579
+ }
7580
+ )
7581
+ }
7582
+ );
7583
+ }
7584
+ MarkdownRenderer.displayName = "MarkdownRenderer";
7585
+ function ImageRenderer({
7586
+ url,
7587
+ fileName,
7588
+ className
7589
+ }) {
7590
+ const [hasError, setHasError] = React20__namespace.useState(false);
7591
+ React20__namespace.useEffect(() => {
7592
+ setHasError(false);
7593
+ }, [url]);
7594
+ if (!url) {
7595
+ return /* @__PURE__ */ jsxRuntime.jsx(
7596
+ "div",
7597
+ {
7598
+ className: cn(
7599
+ "flex h-full w-full items-center justify-center text-sm text-muted-foreground",
7600
+ className
7601
+ ),
7602
+ children: "No image URL provided"
7603
+ }
7604
+ );
7605
+ }
7606
+ if (hasError) {
7607
+ return /* @__PURE__ */ jsxRuntime.jsxs(
7608
+ "div",
7609
+ {
7610
+ className: cn(
7611
+ "flex h-full w-full flex-col items-center justify-center gap-2 text-sm text-muted-foreground",
7612
+ className
7613
+ ),
7614
+ children: [
7615
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Failed to load image" }),
7616
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs", children: fileName })
7617
+ ]
7618
+ }
7619
+ );
7620
+ }
7621
+ return /* @__PURE__ */ jsxRuntime.jsx(
7622
+ "div",
7623
+ {
7624
+ className: cn(
7625
+ "flex h-full w-full items-center justify-center overflow-auto bg-background p-4",
7626
+ "scrollbar-thin",
7627
+ className
7628
+ ),
7629
+ children: /* @__PURE__ */ jsxRuntime.jsx(
7630
+ "img",
7631
+ {
7632
+ src: url,
7633
+ alt: fileName,
7634
+ onError: () => setHasError(true),
7635
+ className: "max-h-full max-w-full object-contain"
7636
+ }
7637
+ )
7638
+ }
7639
+ );
7640
+ }
7641
+ ImageRenderer.displayName = "ImageRenderer";
7642
+ function PlainTextRenderer({ content, className }) {
7643
+ return /* @__PURE__ */ jsxRuntime.jsx(
7644
+ "div",
7645
+ {
7646
+ className: cn(
7647
+ "h-full w-full overflow-auto rounded-md border border-border bg-background p-4",
7648
+ "scrollbar-thin",
7649
+ className
7650
+ ),
7651
+ children: /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "m-0 whitespace-pre-wrap break-words font-mono text-sm leading-relaxed text-foreground", children: content ?? "" })
7652
+ }
7653
+ );
7654
+ }
7655
+ PlainTextRenderer.displayName = "PlainTextRenderer";
7656
+ function parseCSV(text) {
7657
+ const lines = text.split("\n");
7658
+ if (lines.length === 0) return { headers: [], rows: [] };
7659
+ const firstLine = lines[0] ?? "";
7660
+ const delimiter = firstLine.includes(" ") ? " " : ",";
7661
+ function parseLine(line) {
7662
+ const fields = [];
7663
+ let current = "";
7664
+ let inQuotes = false;
7665
+ let i = 0;
7666
+ while (i < line.length) {
7667
+ const char = line[i];
7668
+ if (inQuotes) {
7669
+ if (char === '"') {
7670
+ if (i + 1 < line.length && line[i + 1] === '"') {
7671
+ current += '"';
7672
+ i += 2;
7673
+ } else {
7674
+ inQuotes = false;
7675
+ i++;
7676
+ }
7677
+ } else {
7678
+ current += char;
7679
+ i++;
7680
+ }
7681
+ } else {
7682
+ if (char === '"') {
7683
+ inQuotes = true;
7684
+ i++;
7685
+ } else if (char === delimiter) {
7686
+ fields.push(current.trim());
7687
+ current = "";
7688
+ i++;
7689
+ } else {
7690
+ current += char;
7691
+ i++;
7692
+ }
7693
+ }
7694
+ }
7695
+ fields.push(current.trim());
7696
+ return fields;
7697
+ }
7698
+ const headers = parseLine(firstLine);
7699
+ const rows = [];
7700
+ for (let i = 1; i < lines.length; i++) {
7701
+ const line = lines[i];
7702
+ if (line.trim() === "") continue;
7703
+ const values = parseLine(line);
7704
+ const row = {};
7705
+ for (let j = 0; j < headers.length; j++) {
7706
+ row[headers[j]] = values[j] ?? "";
7707
+ }
7708
+ rows.push(row);
7709
+ }
7710
+ return { headers, rows };
7711
+ }
7712
+ function CsvRenderer({ content, className }) {
7713
+ const { headers, rows } = React20__namespace.useMemo(
7714
+ () => parseCSV(content ?? ""),
7715
+ [content]
7716
+ );
7717
+ const columns = React20__namespace.useMemo(
7718
+ () => headers.map((header) => ({
7719
+ key: header,
7720
+ header,
7721
+ sortable: true,
7722
+ filterable: true,
7723
+ filterType: "text"
7724
+ })),
7725
+ [headers]
7726
+ );
7727
+ if (headers.length === 0) {
7728
+ return /* @__PURE__ */ jsxRuntime.jsx(
7729
+ "div",
7730
+ {
7731
+ className: cn(
7732
+ "flex h-full w-full items-center justify-center text-sm text-muted-foreground",
7733
+ className
7734
+ ),
7735
+ children: "No CSV data to display"
7736
+ }
7737
+ );
7738
+ }
7739
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("h-full w-full overflow-auto", className), children: /* @__PURE__ */ jsxRuntime.jsx(
7740
+ DataGrid,
7741
+ {
7742
+ data: rows,
7743
+ columns,
7744
+ getRowKey: (_, index) => String(index),
7745
+ resizableColumns: true
7746
+ }
7747
+ ) });
7748
+ }
7749
+ CsvRenderer.displayName = "CsvRenderer";
7750
+ function HtmlRenderer({
7751
+ content,
7752
+ fileName,
7753
+ className
7754
+ }) {
7755
+ return /* @__PURE__ */ jsxRuntime.jsx(
7756
+ "iframe",
7757
+ {
7758
+ srcDoc: content ?? "",
7759
+ sandbox: "allow-scripts",
7760
+ title: fileName,
7761
+ className: cn("h-full w-full border-0", className)
7762
+ }
7763
+ );
7764
+ }
7765
+ HtmlRenderer.displayName = "HtmlRenderer";
7766
+
7767
+ // src/components/file-view/utils/rendererRegistry.ts
7768
+ var DEFAULT_RENDERERS = {
7769
+ code: CodeRenderer,
7770
+ markdown: MarkdownRenderer,
7771
+ image: ImageRenderer,
7772
+ plaintext: PlainTextRenderer,
7773
+ csv: CsvRenderer,
7774
+ html: HtmlRenderer
7775
+ };
7776
+ function mergeRenderers(userRenderers) {
7777
+ if (!userRenderers) return DEFAULT_RENDERERS;
7778
+ return { ...DEFAULT_RENDERERS, ...userRenderers };
7779
+ }
7780
+ function resolveRenderer(registry, contentType) {
7781
+ return registry[contentType] ?? registry["plaintext"] ?? PlainTextRenderer;
7782
+ }
7783
+ function DefaultEmptyState({ message }) {
7784
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full w-full items-center justify-center text-sm text-muted-foreground", children: message });
7785
+ }
7786
+ function DefaultLoadingState() {
7787
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full w-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-3", children: [
7788
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-8 w-8 animate-spin rounded-full border-2 border-primary/20 border-t-primary" }),
7789
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: "Loading file..." })
7790
+ ] }) });
7791
+ }
7792
+ function DefaultErrorState({ error }) {
7793
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full w-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex max-w-sm flex-col items-center gap-3 text-center", children: [
7794
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-destructive", children: error.message }),
7795
+ error.onRetry && /* @__PURE__ */ jsxRuntime.jsx(
7796
+ "button",
7797
+ {
7798
+ onClick: error.onRetry,
7799
+ className: cn(
7800
+ "rounded-md border border-border bg-background px-3 py-1.5 text-sm text-foreground",
7801
+ "transition-colors hover:bg-muted"
7802
+ ),
7803
+ children: "Retry"
7804
+ }
7805
+ )
7806
+ ] }) });
7807
+ }
7808
+ function FileView({
7809
+ content,
7810
+ url = null,
7811
+ fileName,
7812
+ contentType: contentTypeOverride,
7813
+ renderers: userRenderers,
7814
+ loading = false,
7815
+ error = null,
7816
+ loadingComponent,
7817
+ emptyComponent,
7818
+ emptyMessage = "No content to display",
7819
+ errorComponent: ErrorComponent,
7820
+ className,
7821
+ rendererClassName,
7822
+ resolveImageUrl
7823
+ }) {
7824
+ const { type: resolvedType } = useContentType({
7825
+ fileName,
7826
+ contentTypeOverride
7827
+ });
7828
+ const registry = React20__namespace.useMemo(
7829
+ () => mergeRenderers(userRenderers),
7830
+ [userRenderers]
7831
+ );
7832
+ const Renderer = React20__namespace.useMemo(
7833
+ () => resolveRenderer(registry, resolvedType),
7834
+ [registry, resolvedType]
7835
+ );
7836
+ if (loading) {
7837
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("relative h-full w-full", className), children: loadingComponent ?? /* @__PURE__ */ jsxRuntime.jsx(DefaultLoadingState, {}) });
7838
+ }
7839
+ if (error) {
7840
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("relative h-full w-full", className), children: ErrorComponent ? /* @__PURE__ */ jsxRuntime.jsx(ErrorComponent, { error }) : /* @__PURE__ */ jsxRuntime.jsx(DefaultErrorState, { error }) });
7841
+ }
7842
+ if ((content == null || content === "") && url == null) {
7843
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("relative h-full w-full", className), children: emptyComponent ?? /* @__PURE__ */ jsxRuntime.jsx(DefaultEmptyState, { message: emptyMessage }) });
7844
+ }
7845
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("relative h-full w-full", className), children: /* @__PURE__ */ jsxRuntime.jsx(
7846
+ Renderer,
7847
+ {
7848
+ content,
7849
+ url: url ?? null,
7850
+ fileName,
7851
+ contentType: resolvedType,
7852
+ className: rendererClassName,
7853
+ resolveImageUrl
7854
+ }
7855
+ ) });
7856
+ }
7857
+ FileView.displayName = "FileView";
7858
+
6804
7859
  exports.ALL_THEMES = ALL_THEMES;
6805
7860
  exports.Accordion = Accordion;
6806
7861
  exports.AccordionContent = AccordionContent;
@@ -6838,10 +7893,13 @@ exports.CardTitle = CardTitle;
6838
7893
  exports.CellEditor = CellEditor;
6839
7894
  exports.Checkbox = Checkbox;
6840
7895
  exports.Chip = Chip;
7896
+ exports.CodeRenderer = CodeRenderer;
6841
7897
  exports.ConfirmationModal = ConfirmationModal;
6842
7898
  exports.ContextMenu = ContextMenu;
6843
7899
  exports.CopyButton = CopyButton;
7900
+ exports.CsvRenderer = CsvRenderer;
6844
7901
  exports.DARK_ELEGANT_THEME = DARK_ELEGANT_THEME;
7902
+ exports.DEFAULT_RENDERERS = DEFAULT_RENDERERS;
6845
7903
  exports.DataGrid = DataGrid;
6846
7904
  exports.DataTable = DataTable;
6847
7905
  exports.DatePicker = DatePicker;
@@ -6863,20 +7921,25 @@ exports.DropdownMenuSubTrigger = DropdownMenuSubTrigger;
6863
7921
  exports.DropdownMenuTrigger = DropdownMenuTrigger;
6864
7922
  exports.FOREST_THEME = FOREST_THEME;
6865
7923
  exports.FUTURISTIC_THEME = FUTURISTIC_THEME;
7924
+ exports.FileView = FileView;
6866
7925
  exports.FilterPopover = FilterPopover;
6867
7926
  exports.GREEN_THEME = GREEN_THEME;
6868
7927
  exports.HeaderCell = HeaderCell;
7928
+ exports.HtmlRenderer = HtmlRenderer;
6869
7929
  exports.IconButton = IconButton;
7930
+ exports.ImageRenderer = ImageRenderer;
6870
7931
  exports.Input = Input;
6871
7932
  exports.Label = Label;
6872
7933
  exports.LoadingSpinner = LoadingSpinner;
6873
7934
  exports.MINIMALIST_LIGHT_THEME = MINIMALIST_LIGHT_THEME;
7935
+ exports.MarkdownRenderer = MarkdownRenderer;
6874
7936
  exports.Modal = Modal;
6875
7937
  exports.ModalButton = ModalButton;
6876
7938
  exports.NATURE_THEME = NATURE_THEME;
6877
7939
  exports.OCEAN_THEME = OCEAN_THEME;
6878
7940
  exports.OPTILOGIC_LEGACY_THEME = OPTILOGIC_LEGACY_THEME;
6879
7941
  exports.PRESET_THEMES = PRESET_THEMES;
7942
+ exports.PlainTextRenderer = PlainTextRenderer;
6880
7943
  exports.Popover = Popover;
6881
7944
  exports.PopoverAnchor = PopoverAnchor;
6882
7945
  exports.PopoverContent = PopoverContent;
@@ -6940,21 +8003,28 @@ exports.cardListVariants = cardListVariants;
6940
8003
  exports.cardVariants = cardVariants;
6941
8004
  exports.cloneTheme = cloneTheme;
6942
8005
  exports.cn = cn;
8006
+ exports.detectContentType = detectContentType;
6943
8007
  exports.exportTheme = exportTheme;
6944
8008
  exports.getCellValue = getCellValue;
6945
8009
  exports.getCurrentTheme = getCurrentTheme;
6946
8010
  exports.getDefaultTheme = getDefaultTheme;
8011
+ exports.getFileExtension = getFileExtension;
6947
8012
  exports.getPresetTheme = getPresetTheme;
6948
8013
  exports.hexToHsl = hexToHsl;
6949
8014
  exports.iconButtonVariants = iconButtonVariants;
6950
8015
  exports.importTheme = importTheme;
6951
8016
  exports.isPresetTheme = isPresetTheme;
8017
+ exports.isTextContentType = isTextContentType;
8018
+ exports.isUrlContentType = isUrlContentType;
6952
8019
  exports.labelVariants = labelVariants;
6953
8020
  exports.loadingSpinnerVariants = loadingSpinnerVariants;
8021
+ exports.mergeRenderers = mergeRenderers;
8022
+ exports.resolveRenderer = resolveRenderer;
6954
8023
  exports.themeToHsl = themeToHsl;
6955
8024
  exports.useColumnResize = useColumnResize;
6956
8025
  exports.useColumnResizeManager = useColumnResizeManager;
6957
8026
  exports.useConfirmation = useConfirmation;
8027
+ exports.useContentType = useContentType;
6958
8028
  exports.useContextMenu = useContextMenu;
6959
8029
  exports.useDataGridState = useDataGridState;
6960
8030
  exports.useKeyboardNavigation = useKeyboardNavigation;