@optilogic/core 1.0.0-beta.15 → 1.0.0-beta.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -22,6 +22,8 @@ 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) {
@@ -6768,6 +6770,848 @@ function useContextMenu() {
6768
6770
  };
6769
6771
  }
6770
6772
 
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 };
6773
+ // src/components/file-view/utils/contentTypeDetection.ts
6774
+ var EXTENSION_MAP = {
6775
+ // Code / programming languages
6776
+ ts: "code",
6777
+ tsx: "code",
6778
+ js: "code",
6779
+ jsx: "code",
6780
+ mjs: "code",
6781
+ cjs: "code",
6782
+ py: "code",
6783
+ rb: "code",
6784
+ go: "code",
6785
+ rs: "code",
6786
+ java: "code",
6787
+ c: "code",
6788
+ cpp: "code",
6789
+ h: "code",
6790
+ hpp: "code",
6791
+ cs: "code",
6792
+ php: "code",
6793
+ swift: "code",
6794
+ kt: "code",
6795
+ scala: "code",
6796
+ r: "code",
6797
+ sql: "code",
6798
+ sh: "code",
6799
+ bash: "code",
6800
+ zsh: "code",
6801
+ ps1: "code",
6802
+ bat: "code",
6803
+ lua: "code",
6804
+ perl: "code",
6805
+ pl: "code",
6806
+ // Config / data (code-rendered)
6807
+ json: "code",
6808
+ yaml: "code",
6809
+ yml: "code",
6810
+ toml: "code",
6811
+ xml: "code",
6812
+ html: "html",
6813
+ htm: "html",
6814
+ css: "code",
6815
+ scss: "code",
6816
+ sass: "code",
6817
+ less: "code",
6818
+ graphql: "code",
6819
+ gql: "code",
6820
+ // Optimization / domain-specific
6821
+ lp: "code",
6822
+ dat: "code",
6823
+ // Markdown
6824
+ md: "markdown",
6825
+ mdx: "markdown",
6826
+ // Images
6827
+ png: "image",
6828
+ jpg: "image",
6829
+ jpeg: "image",
6830
+ gif: "image",
6831
+ svg: "image",
6832
+ webp: "image",
6833
+ ico: "image",
6834
+ bmp: "image",
6835
+ // PDF
6836
+ pdf: "pdf",
6837
+ // CSV
6838
+ csv: "csv",
6839
+ tsv: "csv",
6840
+ // Plain text
6841
+ txt: "plaintext",
6842
+ log: "plaintext",
6843
+ env: "plaintext"
6844
+ };
6845
+ var SPECIAL_FILES = {
6846
+ dockerfile: "code",
6847
+ makefile: "code",
6848
+ rakefile: "code",
6849
+ gemfile: "code",
6850
+ procfile: "code"
6851
+ };
6852
+ function getFileExtension(fileName) {
6853
+ if (!fileName) return null;
6854
+ const baseName = fileName.split("/").pop() || fileName;
6855
+ if (baseName.startsWith(".") && !baseName.slice(1).includes(".")) {
6856
+ return baseName.slice(1).toLowerCase();
6857
+ }
6858
+ const lastDot = baseName.lastIndexOf(".");
6859
+ if (lastDot === -1 || lastDot === baseName.length - 1) return null;
6860
+ return baseName.slice(lastDot + 1).toLowerCase();
6861
+ }
6862
+ function detectContentType(fileName) {
6863
+ const baseName = (fileName.split("/").pop() || fileName).toLowerCase();
6864
+ const specialType = SPECIAL_FILES[baseName];
6865
+ if (specialType) {
6866
+ return { type: specialType, extension: baseName };
6867
+ }
6868
+ const extension = getFileExtension(fileName);
6869
+ if (!extension) {
6870
+ return { type: "unknown", extension: null };
6871
+ }
6872
+ const type = EXTENSION_MAP[extension] ?? "unknown";
6873
+ return { type, extension };
6874
+ }
6875
+ function isTextContentType(contentType) {
6876
+ return contentType === "code" || contentType === "markdown" || contentType === "plaintext" || contentType === "csv" || contentType === "html" || contentType === "unknown";
6877
+ }
6878
+ function isUrlContentType(contentType) {
6879
+ return contentType === "image" || contentType === "pdf";
6880
+ }
6881
+
6882
+ // src/components/file-view/hooks/useContentType.ts
6883
+ function useContentType({
6884
+ fileName,
6885
+ contentTypeOverride
6886
+ }) {
6887
+ return useMemo(() => {
6888
+ if (contentTypeOverride) {
6889
+ return {
6890
+ type: contentTypeOverride,
6891
+ extension: null,
6892
+ isOverridden: true
6893
+ };
6894
+ }
6895
+ const detected = detectContentType(fileName);
6896
+ return { ...detected, isOverridden: false };
6897
+ }, [fileName, contentTypeOverride]);
6898
+ }
6899
+ var globalHighlighter = null;
6900
+ var initPromise = null;
6901
+ async function getOrCreateHighlighter() {
6902
+ if (globalHighlighter) return globalHighlighter;
6903
+ if (!initPromise) {
6904
+ initPromise = (async () => {
6905
+ try {
6906
+ const { createHighlighter } = await import('shiki');
6907
+ globalHighlighter = await createHighlighter({
6908
+ themes: ["github-light", "github-dark"],
6909
+ langs: []
6910
+ });
6911
+ return globalHighlighter;
6912
+ } catch {
6913
+ return null;
6914
+ }
6915
+ })();
6916
+ }
6917
+ return initPromise;
6918
+ }
6919
+ function useShikiHighlighter() {
6920
+ const [state, setState] = useState({
6921
+ highlighter: globalHighlighter,
6922
+ isReady: globalHighlighter !== null
6923
+ });
6924
+ useEffect(() => {
6925
+ if (globalHighlighter) {
6926
+ setState({ highlighter: globalHighlighter, isReady: true });
6927
+ return;
6928
+ }
6929
+ let cancelled = false;
6930
+ getOrCreateHighlighter().then((h) => {
6931
+ if (!cancelled) {
6932
+ setState({ highlighter: h, isReady: true });
6933
+ }
6934
+ });
6935
+ return () => {
6936
+ cancelled = true;
6937
+ };
6938
+ }, []);
6939
+ return state;
6940
+ }
6941
+ function getBackgroundLightness(el) {
6942
+ const value = getComputedStyle(el).getPropertyValue("--background").trim();
6943
+ if (!value) return null;
6944
+ const match = value.match(/(\d+(?:\.\d+)?)%\s*$/);
6945
+ return match ? parseFloat(match[1]) : null;
6946
+ }
6947
+ function useDarkMode() {
6948
+ const checkDark = useCallback(() => {
6949
+ if (typeof document === "undefined") return false;
6950
+ const lightness = getBackgroundLightness(document.documentElement);
6951
+ if (lightness === null) {
6952
+ return document.documentElement.classList.contains("dark");
6953
+ }
6954
+ return lightness < 50;
6955
+ }, []);
6956
+ const [isDark, setIsDark] = useState(checkDark);
6957
+ useEffect(() => {
6958
+ const el = document.documentElement;
6959
+ const update = () => {
6960
+ setIsDark(checkDark());
6961
+ };
6962
+ const observer = new MutationObserver(update);
6963
+ observer.observe(el, {
6964
+ attributes: true,
6965
+ attributeFilter: ["class", "style"]
6966
+ });
6967
+ update();
6968
+ return () => observer.disconnect();
6969
+ }, [checkDark]);
6970
+ return isDark;
6971
+ }
6972
+
6973
+ // src/components/file-view/hooks/useHighlightedTokens.ts
6974
+ function useHighlightedTokens(code, language) {
6975
+ const { highlighter, isReady } = useShikiHighlighter();
6976
+ const isDark = useDarkMode();
6977
+ const [lines, setLines] = useState(null);
6978
+ useEffect(() => {
6979
+ if (!isReady || !highlighter || !code) {
6980
+ setLines(null);
6981
+ return;
6982
+ }
6983
+ let cancelled = false;
6984
+ const theme = isDark ? "github-dark" : "github-light";
6985
+ (async () => {
6986
+ try {
6987
+ if (language !== "text") {
6988
+ const loadedLangs = highlighter.getLoadedLanguages();
6989
+ if (!loadedLangs.includes(language)) {
6990
+ await highlighter.loadLanguage(language);
6991
+ }
6992
+ }
6993
+ } catch {
6994
+ if (!cancelled) setLines(null);
6995
+ return;
6996
+ }
6997
+ if (cancelled) return;
6998
+ try {
6999
+ const result = highlighter.codeToTokens(code, {
7000
+ lang: language,
7001
+ theme
7002
+ });
7003
+ if (!cancelled) {
7004
+ setLines(
7005
+ result.tokens.map(
7006
+ (line) => line.map((token) => ({
7007
+ content: token.content,
7008
+ color: token.color
7009
+ }))
7010
+ )
7011
+ );
7012
+ }
7013
+ } catch {
7014
+ if (!cancelled) setLines(null);
7015
+ }
7016
+ })();
7017
+ return () => {
7018
+ cancelled = true;
7019
+ };
7020
+ }, [highlighter, isReady, code, language, isDark]);
7021
+ return { lines };
7022
+ }
7023
+
7024
+ // src/components/file-view/utils/languageMapping.ts
7025
+ var EXTENSION_OVERRIDES = {
7026
+ yml: "yaml",
7027
+ htm: "html",
7028
+ cjs: "javascript",
7029
+ mjs: "javascript",
7030
+ jsx: "jsx",
7031
+ tsx: "tsx",
7032
+ h: "c",
7033
+ hpp: "cpp",
7034
+ cs: "csharp",
7035
+ rb: "ruby",
7036
+ sh: "shellscript",
7037
+ bash: "shellscript",
7038
+ zsh: "shellscript",
7039
+ ps1: "powershell",
7040
+ bat: "bat",
7041
+ kt: "kotlin",
7042
+ rs: "rust",
7043
+ gql: "graphql",
7044
+ pl: "perl",
7045
+ sass: "sass",
7046
+ scss: "scss",
7047
+ less: "less",
7048
+ // Domain-specific / data formats without shiki support
7049
+ lp: "text",
7050
+ dat: "text",
7051
+ env: "shellscript"
7052
+ };
7053
+ var SPECIAL_FILE_LANGUAGES = {
7054
+ dockerfile: "dockerfile",
7055
+ makefile: "makefile",
7056
+ rakefile: "ruby",
7057
+ gemfile: "ruby",
7058
+ procfile: "yaml"
7059
+ };
7060
+ function getLanguageFromFileName(fileName) {
7061
+ const baseName = (fileName.split("/").pop() || fileName).toLowerCase();
7062
+ const specialLang = SPECIAL_FILE_LANGUAGES[baseName];
7063
+ if (specialLang) return specialLang;
7064
+ let ext = null;
7065
+ if (baseName.startsWith(".") && !baseName.slice(1).includes(".")) {
7066
+ ext = baseName.slice(1);
7067
+ } else {
7068
+ const lastDot = baseName.lastIndexOf(".");
7069
+ if (lastDot !== -1 && lastDot !== baseName.length - 1) {
7070
+ ext = baseName.slice(lastDot + 1);
7071
+ }
7072
+ }
7073
+ if (!ext) return "text";
7074
+ return EXTENSION_OVERRIDES[ext] ?? ext;
7075
+ }
7076
+ function CodeRenderer({
7077
+ content,
7078
+ fileName,
7079
+ className
7080
+ }) {
7081
+ const language = React20.useMemo(
7082
+ () => getLanguageFromFileName(fileName),
7083
+ [fileName]
7084
+ );
7085
+ const plainLines = React20.useMemo(
7086
+ () => (content ?? "").split("\n"),
7087
+ [content]
7088
+ );
7089
+ const { lines: highlightedLines } = useHighlightedTokens(
7090
+ content ?? "",
7091
+ language
7092
+ );
7093
+ const gutterWidth = React20.useMemo(
7094
+ () => `${Math.max(String(plainLines.length).length, 2) + 2}ch`,
7095
+ [plainLines.length]
7096
+ );
7097
+ return /* @__PURE__ */ jsx(
7098
+ "div",
7099
+ {
7100
+ className: cn(
7101
+ "relative h-full w-full overflow-auto rounded-md border border-border bg-background",
7102
+ "scrollbar-thin",
7103
+ className
7104
+ ),
7105
+ 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: [
7106
+ /* @__PURE__ */ jsx(
7107
+ "span",
7108
+ {
7109
+ className: "sticky left-0 shrink-0 select-none border-r border-border bg-muted px-3 text-right text-muted-foreground",
7110
+ style: { minWidth: gutterWidth },
7111
+ children: index + 1
7112
+ }
7113
+ ),
7114
+ /* @__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)) })
7115
+ ] }, index)) : plainLines.map((line, index) => /* @__PURE__ */ jsxs("div", { className: "flex", children: [
7116
+ /* @__PURE__ */ jsx(
7117
+ "span",
7118
+ {
7119
+ className: "sticky left-0 shrink-0 select-none border-r border-border bg-muted px-3 text-right text-muted-foreground",
7120
+ style: { minWidth: gutterWidth },
7121
+ children: index + 1
7122
+ }
7123
+ ),
7124
+ /* @__PURE__ */ jsx("span", { className: "whitespace-pre px-4 text-foreground", children: line || " " })
7125
+ ] }, index)) }) })
7126
+ }
7127
+ );
7128
+ }
7129
+ CodeRenderer.displayName = "CodeRenderer";
7130
+ function HighlightedCodeBlock({
7131
+ children,
7132
+ className: codeClassName,
7133
+ ...props
7134
+ }) {
7135
+ const language = React20.useMemo(() => {
7136
+ if (!codeClassName) return "text";
7137
+ const match = codeClassName.match(/language-(\S+)/);
7138
+ return match ? match[1] : "text";
7139
+ }, [codeClassName]);
7140
+ const code = React20.useMemo(() => {
7141
+ const raw = String(children);
7142
+ return raw.endsWith("\n") ? raw.slice(0, -1) : raw;
7143
+ }, [children]);
7144
+ const { lines } = useHighlightedTokens(code, language);
7145
+ if (lines) {
7146
+ return /* @__PURE__ */ jsx("code", { className: cn("bg-transparent p-0", codeClassName), ...props, children: lines.map((tokens, i) => /* @__PURE__ */ jsxs(React20.Fragment, { children: [
7147
+ i > 0 && "\n",
7148
+ tokens.map((token, ti) => /* @__PURE__ */ jsx("span", { style: { color: token.color }, children: token.content }, ti))
7149
+ ] }, i)) });
7150
+ }
7151
+ return /* @__PURE__ */ jsx("code", { className: cn("bg-transparent p-0", codeClassName), ...props, children });
7152
+ }
7153
+ function MarkdownImage({
7154
+ src,
7155
+ alt,
7156
+ resolveImageUrl,
7157
+ ...props
7158
+ }) {
7159
+ const needsResolution = !!src && !!resolveImageUrl && !src.startsWith("http://") && !src.startsWith("https://") && !src.startsWith("data:");
7160
+ const [resolvedUrl, setResolvedUrl] = React20.useState(null);
7161
+ const [error, setError] = React20.useState(false);
7162
+ const [loading, setLoading] = React20.useState(needsResolution);
7163
+ React20.useEffect(() => {
7164
+ if (!needsResolution || !src || !resolveImageUrl) return;
7165
+ let cancelled = false;
7166
+ let blobUrl = null;
7167
+ setLoading(true);
7168
+ setError(false);
7169
+ setResolvedUrl(null);
7170
+ Promise.resolve(resolveImageUrl(src)).then((url) => {
7171
+ if (cancelled) {
7172
+ if (url.startsWith("blob:")) URL.revokeObjectURL(url);
7173
+ return;
7174
+ }
7175
+ if (url.startsWith("blob:")) blobUrl = url;
7176
+ setResolvedUrl(url);
7177
+ setLoading(false);
7178
+ }).catch(() => {
7179
+ if (!cancelled) {
7180
+ setError(true);
7181
+ setLoading(false);
7182
+ }
7183
+ });
7184
+ return () => {
7185
+ cancelled = true;
7186
+ if (blobUrl) URL.revokeObjectURL(blobUrl);
7187
+ };
7188
+ }, [needsResolution, src, resolveImageUrl]);
7189
+ if (!needsResolution) {
7190
+ return /* @__PURE__ */ jsx("img", { src, alt, className: "max-w-full rounded", ...props });
7191
+ }
7192
+ if (loading) {
7193
+ return /* @__PURE__ */ jsx("span", { className: "my-2 inline-block h-24 w-40 animate-pulse rounded bg-muted" });
7194
+ }
7195
+ if (error || !resolvedUrl) {
7196
+ return /* @__PURE__ */ jsx("img", { src, alt, className: "max-w-full rounded", ...props });
7197
+ }
7198
+ return /* @__PURE__ */ jsx(
7199
+ "img",
7200
+ {
7201
+ src: resolvedUrl,
7202
+ alt,
7203
+ className: "max-w-full rounded",
7204
+ ...props
7205
+ }
7206
+ );
7207
+ }
7208
+ function MarkdownRenderer({
7209
+ content,
7210
+ className,
7211
+ resolveImageUrl
7212
+ }) {
7213
+ const markdownComponents = React20.useMemo(
7214
+ () => ({
7215
+ code: ({
7216
+ children,
7217
+ className: codeClassName,
7218
+ ...props
7219
+ }) => {
7220
+ const isInline = !codeClassName;
7221
+ if (isInline) {
7222
+ return /* @__PURE__ */ jsx(
7223
+ "code",
7224
+ {
7225
+ className: "rounded bg-muted px-1.5 py-0.5 font-mono text-[85%]",
7226
+ ...props,
7227
+ children
7228
+ }
7229
+ );
7230
+ }
7231
+ return /* @__PURE__ */ jsx(HighlightedCodeBlock, { className: codeClassName, ...props, children });
7232
+ },
7233
+ pre: ({ children, ...props }) => /* @__PURE__ */ jsx(
7234
+ "pre",
7235
+ {
7236
+ className: "my-4 overflow-auto rounded-md border border-border bg-muted p-4 font-mono text-sm leading-relaxed",
7237
+ ...props,
7238
+ children
7239
+ }
7240
+ ),
7241
+ blockquote: ({
7242
+ children,
7243
+ ...props
7244
+ }) => /* @__PURE__ */ jsx(
7245
+ "blockquote",
7246
+ {
7247
+ className: "my-4 border-l-4 border-border pl-4 text-muted-foreground",
7248
+ ...props,
7249
+ children
7250
+ }
7251
+ ),
7252
+ a: ({
7253
+ children,
7254
+ href,
7255
+ ...props
7256
+ }) => /* @__PURE__ */ jsx(
7257
+ "a",
7258
+ {
7259
+ href,
7260
+ target: "_blank",
7261
+ rel: "noopener noreferrer",
7262
+ className: "text-primary hover:underline",
7263
+ ...props,
7264
+ children
7265
+ }
7266
+ ),
7267
+ table: ({
7268
+ children,
7269
+ ...props
7270
+ }) => /* @__PURE__ */ jsx("div", { className: "my-4 overflow-auto", children: /* @__PURE__ */ jsx(
7271
+ "table",
7272
+ {
7273
+ className: "w-full border-collapse border border-border text-sm",
7274
+ ...props,
7275
+ children
7276
+ }
7277
+ ) }),
7278
+ th: ({
7279
+ children,
7280
+ ...props
7281
+ }) => /* @__PURE__ */ jsx(
7282
+ "th",
7283
+ {
7284
+ className: "border border-border bg-muted px-3 py-2 text-left font-semibold",
7285
+ ...props,
7286
+ children
7287
+ }
7288
+ ),
7289
+ td: ({
7290
+ children,
7291
+ ...props
7292
+ }) => /* @__PURE__ */ jsx("td", { className: "border border-border px-3 py-2", ...props, children }),
7293
+ img: ({
7294
+ src,
7295
+ alt,
7296
+ ...props
7297
+ }) => /* @__PURE__ */ jsx(
7298
+ MarkdownImage,
7299
+ {
7300
+ src,
7301
+ alt,
7302
+ resolveImageUrl,
7303
+ ...props
7304
+ }
7305
+ )
7306
+ }),
7307
+ [resolveImageUrl]
7308
+ );
7309
+ return /* @__PURE__ */ jsx(
7310
+ "div",
7311
+ {
7312
+ className: cn(
7313
+ "h-full w-full overflow-auto bg-background p-6 text-sm leading-relaxed text-foreground",
7314
+ // Heading styles
7315
+ "[&_h1]:mb-4 [&_h1]:mt-6 [&_h1]:border-b [&_h1]:border-border [&_h1]:pb-2 [&_h1]:text-2xl [&_h1]:font-bold",
7316
+ "[&_h2]:mb-4 [&_h2]:mt-6 [&_h2]:border-b [&_h2]:border-border [&_h2]:pb-2 [&_h2]:text-xl [&_h2]:font-semibold",
7317
+ "[&_h3]:mb-3 [&_h3]:mt-5 [&_h3]:text-lg [&_h3]:font-semibold",
7318
+ "[&_h4]:mb-3 [&_h4]:mt-4 [&_h4]:text-base [&_h4]:font-semibold",
7319
+ // Paragraph and list styles
7320
+ "[&_p]:mb-4 [&_p]:leading-relaxed",
7321
+ "[&_ul]:mb-4 [&_ul]:list-disc [&_ul]:pl-6",
7322
+ "[&_ol]:mb-4 [&_ol]:list-decimal [&_ol]:pl-6",
7323
+ "[&_li]:mb-1",
7324
+ // Horizontal rule
7325
+ "[&_hr]:my-6 [&_hr]:border-border",
7326
+ "scrollbar-thin",
7327
+ className
7328
+ ),
7329
+ children: /* @__PURE__ */ jsx(
7330
+ ReactMarkdownImport,
7331
+ {
7332
+ remarkPlugins: [remarkGfmImport],
7333
+ components: markdownComponents,
7334
+ children: content ?? ""
7335
+ }
7336
+ )
7337
+ }
7338
+ );
7339
+ }
7340
+ MarkdownRenderer.displayName = "MarkdownRenderer";
7341
+ function ImageRenderer({
7342
+ url,
7343
+ fileName,
7344
+ className
7345
+ }) {
7346
+ const [hasError, setHasError] = React20.useState(false);
7347
+ React20.useEffect(() => {
7348
+ setHasError(false);
7349
+ }, [url]);
7350
+ if (!url) {
7351
+ return /* @__PURE__ */ jsx(
7352
+ "div",
7353
+ {
7354
+ className: cn(
7355
+ "flex h-full w-full items-center justify-center text-sm text-muted-foreground",
7356
+ className
7357
+ ),
7358
+ children: "No image URL provided"
7359
+ }
7360
+ );
7361
+ }
7362
+ if (hasError) {
7363
+ return /* @__PURE__ */ jsxs(
7364
+ "div",
7365
+ {
7366
+ className: cn(
7367
+ "flex h-full w-full flex-col items-center justify-center gap-2 text-sm text-muted-foreground",
7368
+ className
7369
+ ),
7370
+ children: [
7371
+ /* @__PURE__ */ jsx("span", { children: "Failed to load image" }),
7372
+ /* @__PURE__ */ jsx("span", { className: "text-xs", children: fileName })
7373
+ ]
7374
+ }
7375
+ );
7376
+ }
7377
+ return /* @__PURE__ */ jsx(
7378
+ "div",
7379
+ {
7380
+ className: cn(
7381
+ "flex h-full w-full items-center justify-center overflow-auto bg-background p-4",
7382
+ "scrollbar-thin",
7383
+ className
7384
+ ),
7385
+ children: /* @__PURE__ */ jsx(
7386
+ "img",
7387
+ {
7388
+ src: url,
7389
+ alt: fileName,
7390
+ onError: () => setHasError(true),
7391
+ className: "max-h-full max-w-full object-contain"
7392
+ }
7393
+ )
7394
+ }
7395
+ );
7396
+ }
7397
+ ImageRenderer.displayName = "ImageRenderer";
7398
+ function PlainTextRenderer({ content, className }) {
7399
+ return /* @__PURE__ */ jsx(
7400
+ "div",
7401
+ {
7402
+ className: cn(
7403
+ "h-full w-full overflow-auto rounded-md border border-border bg-background p-4",
7404
+ "scrollbar-thin",
7405
+ className
7406
+ ),
7407
+ children: /* @__PURE__ */ jsx("pre", { className: "m-0 whitespace-pre-wrap break-words font-mono text-sm leading-relaxed text-foreground", children: content ?? "" })
7408
+ }
7409
+ );
7410
+ }
7411
+ PlainTextRenderer.displayName = "PlainTextRenderer";
7412
+ function parseCSV(text) {
7413
+ const lines = text.split("\n");
7414
+ if (lines.length === 0) return { headers: [], rows: [] };
7415
+ const firstLine = lines[0] ?? "";
7416
+ const delimiter = firstLine.includes(" ") ? " " : ",";
7417
+ function parseLine(line) {
7418
+ const fields = [];
7419
+ let current = "";
7420
+ let inQuotes = false;
7421
+ let i = 0;
7422
+ while (i < line.length) {
7423
+ const char = line[i];
7424
+ if (inQuotes) {
7425
+ if (char === '"') {
7426
+ if (i + 1 < line.length && line[i + 1] === '"') {
7427
+ current += '"';
7428
+ i += 2;
7429
+ } else {
7430
+ inQuotes = false;
7431
+ i++;
7432
+ }
7433
+ } else {
7434
+ current += char;
7435
+ i++;
7436
+ }
7437
+ } else {
7438
+ if (char === '"') {
7439
+ inQuotes = true;
7440
+ i++;
7441
+ } else if (char === delimiter) {
7442
+ fields.push(current.trim());
7443
+ current = "";
7444
+ i++;
7445
+ } else {
7446
+ current += char;
7447
+ i++;
7448
+ }
7449
+ }
7450
+ }
7451
+ fields.push(current.trim());
7452
+ return fields;
7453
+ }
7454
+ const headers = parseLine(firstLine);
7455
+ const rows = [];
7456
+ for (let i = 1; i < lines.length; i++) {
7457
+ const line = lines[i];
7458
+ if (line.trim() === "") continue;
7459
+ const values = parseLine(line);
7460
+ const row = {};
7461
+ for (let j = 0; j < headers.length; j++) {
7462
+ row[headers[j]] = values[j] ?? "";
7463
+ }
7464
+ rows.push(row);
7465
+ }
7466
+ return { headers, rows };
7467
+ }
7468
+ function CsvRenderer({ content, className }) {
7469
+ const { headers, rows } = React20.useMemo(
7470
+ () => parseCSV(content ?? ""),
7471
+ [content]
7472
+ );
7473
+ const columns = React20.useMemo(
7474
+ () => headers.map((header) => ({
7475
+ key: header,
7476
+ header,
7477
+ sortable: true,
7478
+ filterable: true,
7479
+ filterType: "text"
7480
+ })),
7481
+ [headers]
7482
+ );
7483
+ if (headers.length === 0) {
7484
+ return /* @__PURE__ */ jsx(
7485
+ "div",
7486
+ {
7487
+ className: cn(
7488
+ "flex h-full w-full items-center justify-center text-sm text-muted-foreground",
7489
+ className
7490
+ ),
7491
+ children: "No CSV data to display"
7492
+ }
7493
+ );
7494
+ }
7495
+ return /* @__PURE__ */ jsx("div", { className: cn("h-full w-full overflow-auto", className), children: /* @__PURE__ */ jsx(
7496
+ DataGrid,
7497
+ {
7498
+ data: rows,
7499
+ columns,
7500
+ getRowKey: (_, index) => String(index),
7501
+ resizableColumns: true
7502
+ }
7503
+ ) });
7504
+ }
7505
+ CsvRenderer.displayName = "CsvRenderer";
7506
+ function HtmlRenderer({
7507
+ content,
7508
+ fileName,
7509
+ className
7510
+ }) {
7511
+ return /* @__PURE__ */ jsx(
7512
+ "iframe",
7513
+ {
7514
+ srcDoc: content ?? "",
7515
+ sandbox: "",
7516
+ title: fileName,
7517
+ className: cn("h-full w-full border-0", className)
7518
+ }
7519
+ );
7520
+ }
7521
+ HtmlRenderer.displayName = "HtmlRenderer";
7522
+
7523
+ // src/components/file-view/utils/rendererRegistry.ts
7524
+ var DEFAULT_RENDERERS = {
7525
+ code: CodeRenderer,
7526
+ markdown: MarkdownRenderer,
7527
+ image: ImageRenderer,
7528
+ plaintext: PlainTextRenderer,
7529
+ csv: CsvRenderer,
7530
+ html: HtmlRenderer
7531
+ };
7532
+ function mergeRenderers(userRenderers) {
7533
+ if (!userRenderers) return DEFAULT_RENDERERS;
7534
+ return { ...DEFAULT_RENDERERS, ...userRenderers };
7535
+ }
7536
+ function resolveRenderer(registry, contentType) {
7537
+ return registry[contentType] ?? registry["plaintext"] ?? PlainTextRenderer;
7538
+ }
7539
+ function DefaultEmptyState({ message }) {
7540
+ return /* @__PURE__ */ jsx("div", { className: "flex h-full w-full items-center justify-center text-sm text-muted-foreground", children: message });
7541
+ }
7542
+ function DefaultLoadingState() {
7543
+ 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: [
7544
+ /* @__PURE__ */ jsx("div", { className: "h-8 w-8 animate-spin rounded-full border-2 border-primary/20 border-t-primary" }),
7545
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Loading file..." })
7546
+ ] }) });
7547
+ }
7548
+ function DefaultErrorState({ error }) {
7549
+ 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: [
7550
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-destructive", children: error.message }),
7551
+ error.onRetry && /* @__PURE__ */ jsx(
7552
+ "button",
7553
+ {
7554
+ onClick: error.onRetry,
7555
+ className: cn(
7556
+ "rounded-md border border-border bg-background px-3 py-1.5 text-sm text-foreground",
7557
+ "transition-colors hover:bg-muted"
7558
+ ),
7559
+ children: "Retry"
7560
+ }
7561
+ )
7562
+ ] }) });
7563
+ }
7564
+ function FileView({
7565
+ content,
7566
+ url = null,
7567
+ fileName,
7568
+ contentType: contentTypeOverride,
7569
+ renderers: userRenderers,
7570
+ loading = false,
7571
+ error = null,
7572
+ loadingComponent,
7573
+ emptyComponent,
7574
+ emptyMessage = "No content to display",
7575
+ errorComponent: ErrorComponent,
7576
+ className,
7577
+ rendererClassName,
7578
+ resolveImageUrl
7579
+ }) {
7580
+ const { type: resolvedType } = useContentType({
7581
+ fileName,
7582
+ contentTypeOverride
7583
+ });
7584
+ const registry = React20.useMemo(
7585
+ () => mergeRenderers(userRenderers),
7586
+ [userRenderers]
7587
+ );
7588
+ const Renderer = React20.useMemo(
7589
+ () => resolveRenderer(registry, resolvedType),
7590
+ [registry, resolvedType]
7591
+ );
7592
+ if (loading) {
7593
+ return /* @__PURE__ */ jsx("div", { className: cn("relative h-full w-full", className), children: loadingComponent ?? /* @__PURE__ */ jsx(DefaultLoadingState, {}) });
7594
+ }
7595
+ if (error) {
7596
+ return /* @__PURE__ */ jsx("div", { className: cn("relative h-full w-full", className), children: ErrorComponent ? /* @__PURE__ */ jsx(ErrorComponent, { error }) : /* @__PURE__ */ jsx(DefaultErrorState, { error }) });
7597
+ }
7598
+ if ((content == null || content === "") && url == null) {
7599
+ return /* @__PURE__ */ jsx("div", { className: cn("relative h-full w-full", className), children: emptyComponent ?? /* @__PURE__ */ jsx(DefaultEmptyState, { message: emptyMessage }) });
7600
+ }
7601
+ return /* @__PURE__ */ jsx("div", { className: cn("relative h-full w-full", className), children: /* @__PURE__ */ jsx(
7602
+ Renderer,
7603
+ {
7604
+ content,
7605
+ url: url ?? null,
7606
+ fileName,
7607
+ contentType: resolvedType,
7608
+ className: rendererClassName,
7609
+ resolveImageUrl
7610
+ }
7611
+ ) });
7612
+ }
7613
+ FileView.displayName = "FileView";
7614
+
7615
+ 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
7616
  //# sourceMappingURL=index.js.map
6773
7617
  //# sourceMappingURL=index.js.map