Skip to content

Commit 8e52133

Browse files
committed
feat(site): wire viewerActions slot + stub /sql route
Demonstrates the new lib hook end-to-end: - `viewerActions.tsx` — returns an `↗ SQL` link for parquet / csv / tsv kinds, pointing at `/sql?url=<getUrl(path)>`. Wired into the three FileTree mounts (MockDemo, HttpDemo, BucketBrowser). - `SqlStub` — placeholder `/sql` route that echoes the requested URL inside a suggested `SELECT * FROM read_parquet(...) LIMIT 100` query. A real consumer would mount a DuckDB-WASM REPL here (see crashes' `SqlPage` for a reference impl).
1 parent 8741e6b commit 8e52133

6 files changed

Lines changed: 74 additions & 0 deletions

File tree

site/src/App.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { MockDemo } from './routes/MockDemo'
44
import { HttpDemo } from './routes/HttpDemo'
55
import { S3Demo } from './routes/S3Demo'
66
import { R2Demo } from './routes/R2Demo'
7+
import { SqlStub } from './routes/SqlStub'
78

89
export function App() {
910
return (
@@ -29,6 +30,7 @@ export function App() {
2930
<Route path="/s3/:slug/*" element={<S3Demo />} />
3031
<Route path="/r2" element={<R2Demo />} />
3132
<Route path="/r2/:slug/*" element={<R2Demo />} />
33+
<Route path="/sql" element={<SqlStub />} />
3234
</Routes>
3335
</main>
3436
</>

site/src/components/BucketBrowser.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { renderJsonTree } from '../JsonTree'
1515
import { CsvViewer } from '../CsvViewer'
1616
import { NotebookViewer } from '../NotebookViewer'
1717
import { renderCode } from '../CodeHighlight'
18+
import { renderViewerActions } from '../viewerActions'
1819
import type { BucketEntry } from './BucketsPage'
1920

2021
export interface BucketBrowserProps {
@@ -94,6 +95,7 @@ export function BucketBrowser({ seeds, lsKey, routeBase, buildStore }: BucketBro
9495
csvRenderer={CsvViewer}
9596
notebookRenderer={NotebookViewer}
9697
codeRenderer={renderCode}
98+
viewerActions={renderViewerActions}
9799
/>
98100
</div>
99101
)

site/src/routes/HttpDemo.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { renderJsonTree } from '../JsonTree'
88
import { CsvViewer } from '../CsvViewer'
99
import { NotebookViewer } from '../NotebookViewer'
1010
import { renderCode } from '../CodeHighlight'
11+
import { renderViewerActions } from '../viewerActions'
1112

1213
// Default points at the deployed demo worker (CFW, multi-bucket).
1314
// Override via `VITE_HTTP_DEMO_BASE` (e.g. `http://localhost:8732/v1/files`
@@ -31,6 +32,7 @@ export function HttpDemo() {
3132
csvRenderer={CsvViewer}
3233
notebookRenderer={NotebookViewer}
3334
codeRenderer={renderCode}
35+
viewerActions={renderViewerActions}
3436
/>
3537
{atVirtualRoot && (
3638
<aside style={{ marginTop: '1em', fontSize: '0.9em', opacity: 0.85 }}>

site/src/routes/MockDemo.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { renderJsonTree } from '../JsonTree'
88
import { CsvViewer } from '../CsvViewer'
99
import { NotebookViewer } from '../NotebookViewer'
1010
import { renderCode } from '../CodeHighlight'
11+
import { renderViewerActions } from '../viewerActions'
1112

1213
export function MockDemo() {
1314
const store = useMemo(() => MockStore(DEMO_FIXTURE, { pageSize: 100 }), [])
@@ -23,6 +24,7 @@ export function MockDemo() {
2324
csvRenderer={CsvViewer}
2425
notebookRenderer={NotebookViewer}
2526
codeRenderer={renderCode}
27+
viewerActions={renderViewerActions}
2628
/>
2729
<details style={{ marginTop: '2em', fontSize: '0.9em', opacity: 0.85 }}>
2830
<summary>How this works</summary>

site/src/routes/SqlStub.tsx

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/** Stub `/sql` route. Echoes the `?url=...` query param so the demo can
2+
* prove `viewerActions` wires through end-to-end. A real consumer
3+
* would mount a DuckDB-WASM REPL here (see crashes' `SqlPage` for a
4+
* reference impl). */
5+
import { useSearchParams } from 'react-router-dom'
6+
7+
export function SqlStub() {
8+
const [params] = useSearchParams()
9+
const url = params.get('url') ?? ''
10+
return (
11+
<div style={{ maxWidth: 900, margin: '0 auto', padding: '1.5em' }}>
12+
<h1 style={{ fontSize: '1.4em', margin: '0 0 0.3em' }}>SQL (stub)</h1>
13+
<p style={{ fontSize: '0.95em', opacity: 0.8, margin: '0 0 0.8em' }}>
14+
Placeholder route for the <code>viewerActions</code> demo. A real consumer would mount a
15+
DuckDB-WASM REPL here and run e.g.
16+
</p>
17+
{url ? (
18+
<pre style={{
19+
background: 'rgba(127,127,127,0.08)',
20+
padding: '0.6em 0.8em',
21+
borderRadius: 4,
22+
overflow: 'auto',
23+
fontSize: '0.85em',
24+
fontFamily: 'ui-monospace, monospace',
25+
whiteSpace: 'pre-wrap',
26+
}}>{`SELECT * FROM ${guessReader(url)}('${url}') LIMIT 100;`}</pre>
27+
) : (
28+
<p style={{ opacity: 0.7 }}>No <code>?url=...</code> provided.</p>
29+
)}
30+
<p style={{ fontSize: '0.85em', opacity: 0.65, marginTop: '1em' }}>
31+
Came from a file viewer? <a href={url} target="_blank" rel="noreferrer">open the raw file</a>.
32+
</p>
33+
</div>
34+
)
35+
}
36+
37+
function guessReader(url: string): string {
38+
if (/\.parquet$|\.pqt$/i.test(url)) return 'read_parquet'
39+
if (/\.csv$/i.test(url)) return 'read_csv'
40+
if (/\.tsv$/i.test(url)) return 'read_csv'
41+
return 'read_csv'
42+
}

site/src/viewerActions.tsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/** Demo `viewerActions` factory. Shows the slot's shape: render
2+
* consumer-specific links next to the download icon, scoped by file
3+
* kind. Here we surface an "↗ open in SQL" link for any tabular
4+
* format (parquet/csv) — wire your own DuckDB-WASM REPL behind the
5+
* `/sql` route to make it functional. */
6+
import { Link } from 'react-router-dom'
7+
import type { ViewerActionCtx } from '@rdub/file-tree/react'
8+
9+
const linkStyle = { fontSize: '0.85em', textDecoration: 'none', opacity: 0.85 }
10+
11+
export function renderViewerActions({ store, path, kind }: ViewerActionCtx) {
12+
if (kind !== 'parquet' && kind !== 'text') return null
13+
// For text we restrict to CSVs / TSVs (parquet is unconditional).
14+
if (kind === 'text' && !/\.(csv|tsv)$/i.test(path)) return null
15+
// Prefer the store's direct URL (the SQL page reads it via DuckDB
16+
// `read_parquet` / `read_csv`); fall back to a relative path that a
17+
// server-side route can resolve.
18+
const url = typeof store.getUrl === 'function' ? store.getUrl(path) : path
19+
return (
20+
<Link to={`/sql?url=${encodeURIComponent(url)}`} title="Open in SQL REPL" style={linkStyle}>
21+
↗ SQL
22+
</Link>
23+
)
24+
}

0 commit comments

Comments
 (0)