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
11 changes: 11 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,18 @@
content="The whole pipeline in your browser: scrub, rinse, soak, steep. Cleaning runs locally; the AI phases are bring-your-own-key."
/>
<meta property="og:type" content="website" />
<meta name="twitter:card" content="summary" />
<meta property="og:url" content="https://datasink.dev/" />
<meta property="og:image" content="https://datasink.dev/og.png" />
<meta property="og:image:width" content="2400" />
<meta property="og:image:height" content="1260" />
<meta property="og:image:alt" content="sink — contact data hygiene for the music industry. scrub, rinse, soak, steep." />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="sink — contact data hygiene for the music industry" />
<meta
name="twitter:description"
content="Scrub, dedupe, enrich and research your contact list — the full sink pipeline, in your browser, bring your own key."
/>
<meta name="twitter:image" content="https://datasink.dev/og.png" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
Expand Down
3 changes: 2 additions & 1 deletion web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"@anthropic-ai/sdk": "^0.39.0",
"datasink": "workspace:*",
"react": "^19.2.0",
"react-dom": "^19.2.0"
"react-dom": "^19.2.0",
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz"
},
"devDependencies": {
"@types/react": "^19.2.0",
Expand Down
Binary file added web/public/og.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 23 additions & 4 deletions web/src/components/DropZone.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export function DropZone({
const [urlValue, setUrlValue] = useState('')
const [spotValue, setSpotValue] = useState('')
const [urlError, setUrlError] = useState<string | null>(null)
const [fileError, setFileError] = useState<string | null>(null)
const [fetching, setFetching] = useState(false)
const fileInput = useRef<HTMLInputElement>(null)

Expand All @@ -31,9 +32,26 @@ export function DropZone({

const readFile = useCallback(
(file: File) => {
setFileError(null)
const reader = new FileReader()
reader.onload = () => submitCsv(String(reader.result ?? ''))
reader.readAsText(file)
if (/\.xlsx?$/i.test(file.name)) {
// Excel — parse the first sheet to CSV, then run the normal pipeline.
reader.onload = async () => {
try {
const XLSX = await import('xlsx')
const wb = XLSX.read(reader.result, { type: 'array' })
const sheet = wb.Sheets[wb.SheetNames[0]]
if (!sheet) throw new Error('empty workbook')
submitCsv(XLSX.utils.sheet_to_csv(sheet))
} catch {
setFileError('Couldn’t read that spreadsheet — try exporting it as CSV.')
}
}
reader.readAsArrayBuffer(file)
} else {
reader.onload = () => submitCsv(String(reader.result ?? ''))
reader.readAsText(file)
}
},
[submitCsv],
)
Expand Down Expand Up @@ -99,7 +117,7 @@ export function DropZone({
<input
ref={fileInput}
type="file"
accept=".csv,text/csv,text/plain"
accept=".csv,.xlsx,.xls,text/csv,text/plain,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-excel"
hidden
onChange={(e) => {
const file = e.target.files?.[0]
Expand All @@ -108,9 +126,10 @@ export function DropZone({
/>
<div className="dropzone-inner">
<div className="dropzone-glyph cyan">◇</div>
<p className="dropzone-title">Drop your contacts CSV here</p>
<p className="dropzone-title">Drop your contacts CSV or Excel file</p>
<p className="dim">click to browse, or paste straight from your spreadsheet</p>
{error && <p className="red dropzone-error">{error}</p>}
{fileError && <p className="red dropzone-error">{fileError}</p>}

<div className="dropzone-extras" onClick={(e) => e.stopPropagation()}>
<button type="button" className="demo-button" onClick={() => submitCsv('__DEMO__')}>
Expand Down
3 changes: 2 additions & 1 deletion web/src/styles/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ html {
body {
font-family: var(--mono);
color: var(--text);
background: linear-gradient(160deg, #06b6d4 0%, #22d3ee 35%, #a5f3fc 70%, #ecfeff 100%);
background: linear-gradient(165deg, #06b6d4 0%, #22d3ee 40%, #67e8f9 72%, #a5f3fc 100%);
background-attachment: fixed;
min-height: 100vh;
}

Expand Down
2 changes: 1 addition & 1 deletion web/vercel.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"buildCommand": "pnpm build",
"buildCommand": "cd .. && pnpm build && pnpm --filter sink-web build",
"outputDirectory": "dist",
"installCommand": "cd .. && pnpm install --frozen-lockfile",
"framework": "vite"
Expand Down
Loading