Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
"watch:url": "NODE_ENV=development nodemon bin/cli.js https://hyperparam.blob.core.windows.net/hyperparam/starcoderdata-js-00000-of-00065.parquet"
},
"dependencies": {
"hightable": "0.17.2",
"hightable": "0.18.1",
"hyparquet": "1.17.1",
"hyparquet-compressors": "1.1.1",
"icebird": "0.3.0",
Expand All @@ -67,8 +67,8 @@
"@storybook/react-vite": "9.0.18",
"@testing-library/react": "16.3.0",
"@types/node": "24.1.0",
"@types/react": "19.1.8",
"@types/react-dom": "19.1.6",
"@types/react": "19.1.9",
"@types/react-dom": "19.1.7",
"@vitejs/plugin-react": "4.7.0",
"@vitest/coverage-v8": "3.2.4",
"eslint": "9.32.0",
Expand Down
4 changes: 2 additions & 2 deletions src/components/AvroView/AvroView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import styles from '../Json/Json.module.css'

interface ViewerProps {
source: FileSource
setError: (error: Error | undefined) => void
setError: (error: unknown) => void
}

/**
Expand Down Expand Up @@ -43,7 +43,7 @@ export default function AvroView({ source, setError }: ViewerProps) {
setContent({ fileSize })
setJson(json)
} catch (error) {
setError(error as Error)
setError(error)
} finally {
setIsLoading(false)
}
Expand Down
17 changes: 6 additions & 11 deletions src/components/Cell/Cell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ interface CellProps {
col: number
}

const UNLOADED_CELL_PLACEHOLDER = '<the content has not been fetched yet>'

/**
* Cell viewer displays a single cell from a table.
*/
Expand All @@ -39,21 +41,14 @@ export default function CellView({ source, row, col }: CellProps) {
const metadata = await parquetMetadataAsync(asyncBuffer)
setProgress(0.75)
const df = parquetDataFrame(from, metadata)
const asyncRows = df.rows({ start: row, end: row + 1 })
if (asyncRows.length > 1 || !(0 in asyncRows)) {
throw new Error(`Expected 1 row, got ${asyncRows.length}`)
}
const asyncRow = asyncRows[0]
// Await cell data

const columnName = df.header[col]
if (columnName === undefined) {
throw new Error(`Column name missing at index col=${col}`)
}
const asyncCell = asyncRow.cells[columnName]
if (asyncCell === undefined) {
throw new Error(`Cell missing at column ${columnName}`)
}
const text = await asyncCell.then(stringify)
await df.fetch({ rowStart: row, rowEnd: row + 1, columns: [columnName] })
const cell = df.getCell({ row, column: columnName })
const text = cell === undefined ? UNLOADED_CELL_PLACEHOLDER : stringify(cell.value)
setText(text)
setError(undefined)
} catch (error) {
Expand Down
70 changes: 43 additions & 27 deletions src/components/CellPanel/CellPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,62 +1,78 @@
import { DataFrame, stringify } from 'hightable'
import { ReactNode, useEffect, useState } from 'react'
import type { DataFrame, ResolvedValue } from 'hightable'
import { stringify } from 'hightable'
import { ReactNode, useCallback, useEffect, useState } from 'react'
import { useConfig } from '../../hooks/useConfig.js'
import { cn } from '../../lib/utils.js'
import ContentWrapper from '../ContentWrapper/ContentWrapper.js'
import Json from '../Json/Json.js'
import jsonStyles from '../Json/Json.module.css'
import SlideCloseButton from '../SlideCloseButton/SlideCloseButton.js'
import styles from '../TextView/TextView.module.css'
import jsonStyles from '../Json/Json.module.css'

interface ViewerProps {
df: DataFrame
row: number
col: number
setProgress: (progress: number) => void
setError: (error: Error) => void
setError: (error: unknown) => void
onClose: () => void
}

const UNLOADED_CELL_PLACEHOLDER = '<the content has not been fetched yet>'

/**
* Cell viewer displays a single cell from a table.
*/
export default function CellPanel({ df, row, col, setProgress, setError, onClose }: ViewerProps) {
const [content, setContent] = useState<ReactNode>()
const { customClass } = useConfig()

const fillContent = useCallback((cell: ResolvedValue<unknown> | undefined) => {
let content: ReactNode
if (cell === undefined) {
content =
<code className={cn(jsonStyles.textView, customClass?.textView)}>
{UNLOADED_CELL_PLACEHOLDER}
</code>
} else {
const { value } = cell
if (value instanceof Object && !(value instanceof Date)) {
content =
<code className={cn(jsonStyles.jsonView, customClass?.jsonView)}>
<Json json={value} />
</code>
} else {
content =
<code className={cn(styles.textView, customClass?.textView)}>
{stringify(value)}
</code>
}
}
setContent(content)
setError(undefined)
}, [customClass?.textView, customClass?.jsonView, setError])

// Load cell data
useEffect(() => {
async function loadCellData() {
try {
setProgress(0.5)
const asyncRows = df.rows({ start: row, end: row + 1 })
if (asyncRows.length > 1 || !(0 in asyncRows)) {
throw new Error(`Expected 1 row, got ${asyncRows.length}`)
}
const asyncRow = asyncRows[0]
// Await cell data

const columnName = df.header[col]
if (columnName === undefined) {
throw new Error(`Column name missing at index col=${col}`)
}
const asyncCell = asyncRow.cells[columnName]
if (asyncCell === undefined) {
throw new Error(`Cell missing at column ${columnName}`)
let cell = df.getCell({ row, column: columnName })
if (cell === undefined) {
fillContent(undefined)
return
}
const value: unknown = await asyncCell
if (value instanceof Object && !(value instanceof Date)) {
setContent(
<code className={cn(jsonStyles.jsonView, customClass?.jsonView)}>
<Json json={value} />
</code>
)
} else {
setContent(
<code className={cn(styles.textView, customClass?.textView)}>
{stringify(value)}
</code>
)
await df.fetch({ rowStart: row, rowEnd: row + 1, columns: [columnName] })
cell = df.getCell({ row, column: columnName })
if (cell === undefined) {
throw new Error(`Cell at row=${row}, column=${columnName} is undefined`)
}
fillContent(cell)
} catch (error) {
setError(error as Error)
} finally {
Expand All @@ -65,7 +81,7 @@ export default function CellPanel({ df, row, col, setProgress, setError, onClose
}

void loadCellData()
}, [df, col, row, setProgress, setError, customClass])
}, [df, col, row, setProgress, setError, fillContent])

const headers = <>
<SlideCloseButton onClick={onClose} />
Expand Down
11 changes: 8 additions & 3 deletions src/components/File/File.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useState } from 'react'
import { useCallback, useState } from 'react'
import type { FileSource } from '../../lib/sources/types.js'
import { toError } from '../../lib/utils.js'
import Breadcrumb from '../Breadcrumb/Breadcrumb.js'
import Layout from '../Layout/Layout.js'
import Viewer from '../Viewer/Viewer.js'
Expand All @@ -13,10 +14,14 @@ interface FileProps {
*/
export default function File({ source }: FileProps) {
const [progress, setProgress] = useState<number>()
const [error, setError] = useState<Error>()
const [error, setError] = useState<Error | undefined>()

const setErrorWrapper = useCallback((error: unknown) => {
setError(toError(error))
}, [setError])

return <Layout progress={progress} error={error} title={source.fileName}>
<Breadcrumb source={source} />
<Viewer source={source} setProgress={setProgress} setError={setError} />
<Viewer source={source} setProgress={setProgress} setError={setErrorWrapper} />
</Layout>
}
4 changes: 2 additions & 2 deletions src/components/ImageView/ImageView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import styles from './ImageView.module.css'

interface ViewerProps {
source: FileSource
setError: (error: Error | undefined) => void
setError: (error: unknown) => void
}

interface Content {
Expand Down Expand Up @@ -45,7 +45,7 @@ export default function ImageView({ source, setError }: ViewerProps) {
setError(undefined)
} catch (error) {
setContent(undefined)
setError(error as Error)
setError(error)
} finally {
setIsLoading(false)
}
Expand Down
4 changes: 2 additions & 2 deletions src/components/JsonView/JsonView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import styles from '../Json/Json.module.css'

interface ViewerProps {
source: FileSource
setError: (error: Error | undefined) => void
setError: (error: unknown) => void
}

const largeFileSize = 8_000_000 // 8 mb
Expand Down Expand Up @@ -48,7 +48,7 @@ export default function JsonView({ source, setError }: ViewerProps) {
setJson(JSON.parse(text))
} catch (error) {
// TODO: show plain text in error case
setError(error as Error)
setError(error)
} finally {
setIsLoading(false)
}
Expand Down
4 changes: 2 additions & 2 deletions src/components/MarkdownView/MarkdownView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import styles from './MarkdownView.module.css'

interface ViewerProps {
source: FileSource
setError: (error: Error | undefined) => void
setError: (error: unknown) => void
}

/**
Expand Down Expand Up @@ -37,7 +37,7 @@ export default function MarkdownView({ source, setError }: ViewerProps) {
setError(undefined)
setContent({ text, fileSize })
} catch (error) {
setError(error as Error)
setError(error)
setContent(undefined)
} finally {
setIsLoading(false)
Expand Down
16 changes: 10 additions & 6 deletions src/components/ParquetView/ParquetView.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import HighTable, { DataFrame, rowCache } from 'hightable'
import HighTable, { DataFrame } from 'hightable'
import 'hightable/src/HighTable.css'
import { asyncBufferFromUrl, parquetMetadataAsync } from 'hyparquet'
import React, { useCallback, useEffect, useState } from 'react'
Expand All @@ -15,7 +15,7 @@ import styles from './ParquetView.module.css'
interface ViewerProps {
source: FileSource
setProgress: (progress: number | undefined) => void
setError: (error: Error | undefined) => void
setError: (error: unknown) => void
}

interface Content extends ContentSize {
Expand All @@ -41,12 +41,11 @@ export default function ParquetView({ source, setProgress, setError }: ViewerPro
const from = { url: resolveUrl, byteLength: asyncBuffer.byteLength, requestInit }
setProgress(0.66)
const metadata = await parquetMetadataAsync(asyncBuffer)
let dataframe = parquetDataFrame(from, metadata)
dataframe = rowCache(dataframe)
const dataframe = parquetDataFrame(from, metadata)
const fileSize = asyncBuffer.byteLength
setContent({ dataframe, fileSize })
} catch (error) {
setError(error as Error)
setError(error)
} finally {
setIsLoading(false)
setProgress(1)
Expand Down Expand Up @@ -83,9 +82,14 @@ export default function ParquetView({ source, setProgress, setError }: ViewerPro
if (cell?.col === col && cell.row === row) {
return undefined
}
const columnName = content?.dataframe.header[col]
if (columnName === undefined || !content?.dataframe.getCell({ row, column: columnName })) {
// don't open the cell panel until it has loaded
return undefined
}
return { row, col }
})
}, [])
}, [content])
const onDoubleClickCell = useCallback((_event: React.MouseEvent, col: number, row: number) => {
toggleCell(col, row)
}, [toggleCell])
Expand Down
4 changes: 2 additions & 2 deletions src/components/TextView/TextView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import styles from './TextView.module.css'

interface ViewerProps {
source: FileSource
setError: (error: Error | undefined) => void
setError: (error: unknown) => void
}

/**
Expand Down Expand Up @@ -36,7 +36,7 @@ export default function TextView({ source, setError }: ViewerProps) {
setError(undefined)
setContent({ text, fileSize })
} catch (error) {
setError(error as Error)
setError(error)
setContent(undefined)
} finally {
setIsLoading(false)
Expand Down
2 changes: 1 addition & 1 deletion src/components/Viewer/Viewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import TextView from '../TextView/TextView.js'

interface ViewerProps {
source: FileSource
setError: (error: Error | undefined) => void
setError: (error: unknown) => void
setProgress: (progress: number | undefined) => void
}

Expand Down
2 changes: 1 addition & 1 deletion src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ export * from './sources/index.js'
export { parquetDataFrame } from './tableProvider.js'
export { asyncBufferFrom, cn, contentTypes, formatFileSize, getFileDate, getFileDateShort, imageTypes, parseFileSize } from './utils.js'
export { parquetQueryWorker } from './workers/parquetWorkerClient.js'
export type { AsyncBufferFrom, Cells } from './workers/types.js'
export type { AsyncBufferFrom } from './workers/types.js'
Loading