/* Copyright 2026 Marimo. All rights reserved. */ import type { Extension } from "@codemirror/state"; import { EditorView } from "@codemirror/view"; import type { PaginationState } from "@tanstack/react-table"; import type React from "react"; import { Suspense, useMemo, useState } from "react"; import { generateColumns, inferFieldTypes, } from "@/components/data-table/columns"; import { DataTable } from "@/components/data-table/data-table"; import { LazyAnyLanguageCodeMirror } from "@/plugins/impl/code/LazyAnyLanguageCodeMirror"; import { parseCsvData } from "@/plugins/impl/vega/loader"; import { useTheme } from "@/theme/useTheme"; import { Arrays } from "@/utils/arrays"; import { type Base64String, base64ToDataURL } from "@/utils/json/base64"; import { Objects } from "@/utils/objects"; const PAGE_SIZE = 25; export function isMediaMime(mime: string): boolean { if (!mime) { return false; } return ( mime.startsWith("image/") || mime.startsWith("audio/") || mime.startsWith("video/") || mime.startsWith("application/pdf") ); } export const MIME_TO_LANGUAGE: Record = { "application/javascript": "javascript", "text/markdown": "markdown", "text/html": "html", "text/css": "css", "text/x-python": "python", "application/json": "json", "application/xml": "xml", "text/x-yaml": "yaml", "text/csv": "markdown", "text/plain": "markdown", default: "markdown", }; /** * Media viewer props: provide either a direct `url` or `base64` + `mime`. * When `url` is set it takes precedence. */ export type MediaSource = | { url: string; base64?: undefined; mime?: string } | { url?: undefined; base64: Base64String; mime: string }; function resolveMediaSrc(source: MediaSource): string { if (source.url != null) { return source.url; } return base64ToDataURL(source.base64, source.mime); } export const CsvViewer: React.FC<{ contents: string }> = ({ contents }) => { const data = useMemo(() => parseCsvData(contents), [contents]); const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: PAGE_SIZE, }); const fieldTypes = useMemo(() => inferFieldTypes(data), [data]); const columns = useMemo( () => generateColumns({ rowHeaders: Arrays.EMPTY, selection: null, fieldTypes, }), [fieldTypes], ); return ( data={data} totalRows={data.length} columns={columns} totalColumns={columns.length} manualPagination={false} paginationState={pagination} setPaginationState={setPagination} wrapperClassName="h-full justify-between pb-1 px-1" pagination={true} className="rounded-none border-b flex flex-col overflow-hidden" rowSelection={Objects.EMPTY} /> ); }; export const ImageViewer: React.FC = (props) => { return Preview; }; export const AudioViewer: React.FC = (props) => { return