feat: add sheet block (#743)
Co-authored-by: Nicolas <nicolascamara29@gmail.com>
This commit is contained in:
parent
93b02a85e1
commit
76804269c4
14 changed files with 419 additions and 6 deletions
115
blocks/sheet.tsx
Normal file
115
blocks/sheet.tsx
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
import { Block } from '@/components/create-block';
|
||||||
|
import {
|
||||||
|
CopyIcon,
|
||||||
|
LineChartIcon,
|
||||||
|
RedoIcon,
|
||||||
|
SparklesIcon,
|
||||||
|
UndoIcon,
|
||||||
|
} from '@/components/icons';
|
||||||
|
import { SpreadsheetEditor } from '@/components/sheet-editor';
|
||||||
|
import { parse, unparse } from 'papaparse';
|
||||||
|
import { toast } from 'sonner';
|
||||||
|
|
||||||
|
type Metadata = any;
|
||||||
|
|
||||||
|
export const sheetBlock = new Block<'sheet', Metadata>({
|
||||||
|
kind: 'sheet',
|
||||||
|
description: 'Useful for working with spreadsheets',
|
||||||
|
initialize: async () => {},
|
||||||
|
onStreamPart: ({ setBlock, streamPart }) => {
|
||||||
|
if (streamPart.type === 'sheet-delta') {
|
||||||
|
setBlock((draftBlock) => ({
|
||||||
|
...draftBlock,
|
||||||
|
content: streamPart.content as string,
|
||||||
|
isVisible: true,
|
||||||
|
status: 'streaming',
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
content: ({
|
||||||
|
content,
|
||||||
|
currentVersionIndex,
|
||||||
|
isCurrentVersion,
|
||||||
|
onSaveContent,
|
||||||
|
status,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<SpreadsheetEditor
|
||||||
|
content={content}
|
||||||
|
currentVersionIndex={currentVersionIndex}
|
||||||
|
isCurrentVersion={isCurrentVersion}
|
||||||
|
saveContent={onSaveContent}
|
||||||
|
status={status}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
icon: <UndoIcon size={18} />,
|
||||||
|
description: 'View Previous version',
|
||||||
|
onClick: ({ handleVersionChange }) => {
|
||||||
|
handleVersionChange('prev');
|
||||||
|
},
|
||||||
|
isDisabled: ({ currentVersionIndex }) => {
|
||||||
|
if (currentVersionIndex === 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <RedoIcon size={18} />,
|
||||||
|
description: 'View Next version',
|
||||||
|
onClick: ({ handleVersionChange }) => {
|
||||||
|
handleVersionChange('next');
|
||||||
|
},
|
||||||
|
isDisabled: ({ isCurrentVersion }) => {
|
||||||
|
if (isCurrentVersion) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: <CopyIcon />,
|
||||||
|
description: 'Copy as .csv',
|
||||||
|
onClick: ({ content }) => {
|
||||||
|
const parsed = parse<string[]>(content, { skipEmptyLines: true });
|
||||||
|
|
||||||
|
const nonEmptyRows = parsed.data.filter((row) =>
|
||||||
|
row.some((cell) => cell.trim() !== ''),
|
||||||
|
);
|
||||||
|
|
||||||
|
const cleanedCsv = unparse(nonEmptyRows);
|
||||||
|
|
||||||
|
navigator.clipboard.writeText(cleanedCsv);
|
||||||
|
toast.success('Copied csv to clipboard!');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
toolbar: [
|
||||||
|
{
|
||||||
|
description: 'Format and clean data',
|
||||||
|
icon: <SparklesIcon />,
|
||||||
|
onClick: ({ appendMessage }) => {
|
||||||
|
appendMessage({
|
||||||
|
role: 'user',
|
||||||
|
content: 'Can you please format and clean the data?',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Analyze and visualize data',
|
||||||
|
icon: <LineChartIcon />,
|
||||||
|
onClick: ({ appendMessage }) => {
|
||||||
|
appendMessage({
|
||||||
|
role: 'user',
|
||||||
|
content:
|
||||||
|
'Can you please analyze and visualize the data by creating a new code block in python?',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
@ -30,8 +30,9 @@ import { textBlock } from '@/blocks/text';
|
||||||
import { imageBlock } from '@/blocks/image';
|
import { imageBlock } from '@/blocks/image';
|
||||||
import { codeBlock } from '@/blocks/code';
|
import { codeBlock } from '@/blocks/code';
|
||||||
import equal from 'fast-deep-equal';
|
import equal from 'fast-deep-equal';
|
||||||
|
import { sheetBlock } from '@/blocks/sheet';
|
||||||
|
|
||||||
export const blockDefinitions = [textBlock, codeBlock, imageBlock] as const;
|
export const blockDefinitions = [textBlock, codeBlock, imageBlock, sheetBlock];
|
||||||
export type BlockKind = (typeof blockDefinitions)[number]['kind'];
|
export type BlockKind = (typeof blockDefinitions)[number]['kind'];
|
||||||
|
|
||||||
export interface UIBlock {
|
export interface UIBlock {
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ export type DataStreamDelta = {
|
||||||
type:
|
type:
|
||||||
| 'text-delta'
|
| 'text-delta'
|
||||||
| 'code-delta'
|
| 'code-delta'
|
||||||
|
| 'sheet-delta'
|
||||||
| 'image-delta'
|
| 'image-delta'
|
||||||
| 'title'
|
| 'title'
|
||||||
| 'id'
|
| 'id'
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import { DocumentToolCall, DocumentToolResult } from './document';
|
||||||
import { CodeEditor } from './code-editor';
|
import { CodeEditor } from './code-editor';
|
||||||
import { useBlock } from '@/hooks/use-block';
|
import { useBlock } from '@/hooks/use-block';
|
||||||
import equal from 'fast-deep-equal';
|
import equal from 'fast-deep-equal';
|
||||||
|
import { SpreadsheetEditor } from './sheet-editor';
|
||||||
import { ImageEditor } from './image-editor';
|
import { ImageEditor } from './image-editor';
|
||||||
|
|
||||||
interface DocumentPreviewProps {
|
interface DocumentPreviewProps {
|
||||||
|
|
@ -43,6 +44,7 @@ export function DocumentPreview({
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const boundingBox = hitboxRef.current?.getBoundingClientRect();
|
const boundingBox = hitboxRef.current?.getBoundingClientRect();
|
||||||
|
|
||||||
if (block.documentId && boundingBox) {
|
if (block.documentId && boundingBox) {
|
||||||
setBlock((block) => ({
|
setBlock((block) => ({
|
||||||
...block,
|
...block,
|
||||||
|
|
@ -256,6 +258,12 @@ const DocumentContent = ({ document }: { document: Document }) => {
|
||||||
<CodeEditor {...commonProps} onSaveContent={() => {}} />
|
<CodeEditor {...commonProps} onSaveContent={() => {}} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
) : document.kind === 'sheet' ? (
|
||||||
|
<div className="flex flex-1 relative size-full p-4">
|
||||||
|
<div className="absolute inset-0">
|
||||||
|
<SpreadsheetEditor {...commonProps} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
) : document.kind === 'image' ? (
|
) : document.kind === 'image' ? (
|
||||||
<ImageEditor
|
<ImageEditor
|
||||||
title={document.title}
|
title={document.title}
|
||||||
|
|
|
||||||
|
|
@ -1119,3 +1119,37 @@ export const FullscreenIcon = ({ size = 16 }: { size?: number }) => (
|
||||||
></path>
|
></path>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const DownloadIcon = ({ size = 16 }: { size?: number }) => (
|
||||||
|
<svg
|
||||||
|
height={size}
|
||||||
|
strokeLinejoin="round"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
width={size}
|
||||||
|
style={{ color: 'currentcolor' }}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M8.75 1V1.75V8.68934L10.7197 6.71967L11.25 6.18934L12.3107 7.25L11.7803 7.78033L8.70711 10.8536C8.31658 11.2441 7.68342 11.2441 7.29289 10.8536L4.21967 7.78033L3.68934 7.25L4.75 6.18934L5.28033 6.71967L7.25 8.68934V1.75V1H8.75ZM13.5 9.25V13.5H2.5V9.25V8.5H1V9.25V14C1 14.5523 1.44771 15 2 15H14C14.5523 15 15 14.5523 15 14V9.25V8.5H13.5V9.25Z"
|
||||||
|
fill="currentColor"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const LineChartIcon = ({ size = 16 }: { size?: number }) => (
|
||||||
|
<svg
|
||||||
|
height={size}
|
||||||
|
strokeLinejoin="round"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
width={size}
|
||||||
|
style={{ color: 'currentcolor' }}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
fillRule="evenodd"
|
||||||
|
d="M1 1v11.75A2.25 2.25 0 0 0 3.25 15H15v-1.5H3.25a.75.75 0 0 1-.75-.75V1H1Zm13.297 5.013.513-.547-1.094-1.026-.513.547-3.22 3.434-2.276-2.275a1 1 0 0 0-1.414 0L4.22 8.22l-.53.53 1.06 1.06.53-.53L7 7.56l2.287 2.287a1 1 0 0 0 1.437-.023l3.573-3.811Z"
|
||||||
|
clipRule="evenodd"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
|
||||||
143
components/sheet-editor.tsx
Normal file
143
components/sheet-editor.tsx
Normal file
|
|
@ -0,0 +1,143 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import React, { memo, useEffect, useMemo, useState } from 'react';
|
||||||
|
import DataGrid, { textEditor } from 'react-data-grid';
|
||||||
|
import { parse, unparse } from 'papaparse';
|
||||||
|
import { useTheme } from 'next-themes';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
import 'react-data-grid/lib/styles.css';
|
||||||
|
|
||||||
|
type SheetEditorProps = {
|
||||||
|
content: string;
|
||||||
|
saveContent: (content: string, isCurrentVersion: boolean) => void;
|
||||||
|
status: string;
|
||||||
|
isCurrentVersion: boolean;
|
||||||
|
currentVersionIndex: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const MIN_ROWS = 50;
|
||||||
|
const MIN_COLS = 26;
|
||||||
|
|
||||||
|
const PureSpreadsheetEditor = ({
|
||||||
|
content,
|
||||||
|
saveContent,
|
||||||
|
status,
|
||||||
|
isCurrentVersion,
|
||||||
|
}: SheetEditorProps) => {
|
||||||
|
const { theme } = useTheme();
|
||||||
|
|
||||||
|
const parseData = useMemo(() => {
|
||||||
|
if (!content) return Array(MIN_ROWS).fill(Array(MIN_COLS).fill(''));
|
||||||
|
const result = parse<string[]>(content, { skipEmptyLines: true });
|
||||||
|
|
||||||
|
const paddedData = result.data.map((row) => {
|
||||||
|
const paddedRow = [...row];
|
||||||
|
while (paddedRow.length < MIN_COLS) {
|
||||||
|
paddedRow.push('');
|
||||||
|
}
|
||||||
|
return paddedRow;
|
||||||
|
});
|
||||||
|
|
||||||
|
while (paddedData.length < MIN_ROWS) {
|
||||||
|
paddedData.push(Array(MIN_COLS).fill(''));
|
||||||
|
}
|
||||||
|
|
||||||
|
return paddedData;
|
||||||
|
}, [content]);
|
||||||
|
|
||||||
|
const columns = useMemo(() => {
|
||||||
|
const rowNumberColumn = {
|
||||||
|
key: 'rowNumber',
|
||||||
|
name: '',
|
||||||
|
frozen: true,
|
||||||
|
width: 50,
|
||||||
|
renderCell: ({ rowIdx }: { rowIdx: number }) => rowIdx + 1,
|
||||||
|
cellClass: 'border-t border-r dark:bg-zinc-950 dark:text-zinc-50',
|
||||||
|
headerCellClass: 'border-t border-r dark:bg-zinc-900 dark:text-zinc-50',
|
||||||
|
};
|
||||||
|
|
||||||
|
const dataColumns = Array.from({ length: MIN_COLS }, (_, i) => ({
|
||||||
|
key: i.toString(),
|
||||||
|
name: String.fromCharCode(65 + i),
|
||||||
|
renderEditCell: textEditor,
|
||||||
|
width: 120,
|
||||||
|
cellClass: cn(`border-t dark:bg-zinc-950 dark:text-zinc-50`, {
|
||||||
|
'border-l': i !== 0,
|
||||||
|
}),
|
||||||
|
headerCellClass: cn(`border-t dark:bg-zinc-900 dark:text-zinc-50`, {
|
||||||
|
'border-l': i !== 0,
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
return [rowNumberColumn, ...dataColumns];
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const initialRows = useMemo(() => {
|
||||||
|
return parseData.map((row, rowIndex) => {
|
||||||
|
const rowData: any = {
|
||||||
|
id: rowIndex,
|
||||||
|
rowNumber: rowIndex + 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
columns.slice(1).forEach((col, colIndex) => {
|
||||||
|
rowData[col.key] = row[colIndex] || '';
|
||||||
|
});
|
||||||
|
|
||||||
|
return rowData;
|
||||||
|
});
|
||||||
|
}, [parseData, columns]);
|
||||||
|
|
||||||
|
const [localRows, setLocalRows] = useState(initialRows);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setLocalRows(initialRows);
|
||||||
|
}, [initialRows]);
|
||||||
|
|
||||||
|
const generateCsv = (data: any[][]) => {
|
||||||
|
return unparse(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRowsChange = (newRows: any[]) => {
|
||||||
|
setLocalRows(newRows);
|
||||||
|
|
||||||
|
const updatedData = newRows.map((row) => {
|
||||||
|
return columns.slice(1).map((col) => row[col.key] || '');
|
||||||
|
});
|
||||||
|
|
||||||
|
const newCsvContent = generateCsv(updatedData);
|
||||||
|
saveContent(newCsvContent, true);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DataGrid
|
||||||
|
className={theme === 'dark' ? 'rdg-dark' : 'rdg-light'}
|
||||||
|
columns={columns}
|
||||||
|
rows={localRows}
|
||||||
|
enableVirtualization
|
||||||
|
onRowsChange={handleRowsChange}
|
||||||
|
onCellClick={(args) => {
|
||||||
|
if (args.column.key !== 'rowNumber') {
|
||||||
|
args.selectCell(true);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
style={{ height: '100%' }}
|
||||||
|
defaultColumnOptions={{
|
||||||
|
resizable: true,
|
||||||
|
sortable: true,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
function areEqual(prevProps: SheetEditorProps, nextProps: SheetEditorProps) {
|
||||||
|
return (
|
||||||
|
prevProps.currentVersionIndex === nextProps.currentVersionIndex &&
|
||||||
|
prevProps.isCurrentVersion === nextProps.isCurrentVersion &&
|
||||||
|
!(prevProps.status === 'streaming' && nextProps.status === 'streaming') &&
|
||||||
|
prevProps.content === nextProps.content &&
|
||||||
|
prevProps.saveContent === nextProps.saveContent
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SpreadsheetEditor = memo(PureSpreadsheetEditor, areEqual);
|
||||||
|
|
@ -33,6 +33,7 @@ import {
|
||||||
LogsIcon,
|
LogsIcon,
|
||||||
MessageIcon,
|
MessageIcon,
|
||||||
PenIcon,
|
PenIcon,
|
||||||
|
SparklesIcon,
|
||||||
StopIcon,
|
StopIcon,
|
||||||
SummarizeIcon,
|
SummarizeIcon,
|
||||||
} from './icons';
|
} from './icons';
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,10 @@ print(f"Factorial of 5 is: {factorial(5)}")
|
||||||
\`\`\`
|
\`\`\`
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const sheetPrompt = `
|
||||||
|
You are a spreadsheet creation assistant. Create a spreadsheet in csv format based on the given prompt. The spreadsheet should contain meaningful column headers and data.
|
||||||
|
`;
|
||||||
|
|
||||||
export const updateDocumentPrompt = (
|
export const updateDocumentPrompt = (
|
||||||
currentContent: string | null,
|
currentContent: string | null,
|
||||||
type: BlockKind,
|
type: BlockKind,
|
||||||
|
|
@ -78,6 +82,12 @@ ${currentContent}
|
||||||
? `\
|
? `\
|
||||||
Improve the following code snippet based on the given prompt.
|
Improve the following code snippet based on the given prompt.
|
||||||
|
|
||||||
|
${currentContent}
|
||||||
|
`
|
||||||
|
: type === 'sheet'
|
||||||
|
? `\
|
||||||
|
Improve the following spreadsheet based on the given prompt.
|
||||||
|
|
||||||
${currentContent}
|
${currentContent}
|
||||||
`
|
`
|
||||||
: '';
|
: '';
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import {
|
||||||
} from 'ai';
|
} from 'ai';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { customModel, imageGenerationModel } from '..';
|
import { customModel, imageGenerationModel } from '..';
|
||||||
import { codePrompt } from '../prompts';
|
import { codePrompt, sheetPrompt } from '../prompts';
|
||||||
import { saveDocument } from '@/lib/db/queries';
|
import { saveDocument } from '@/lib/db/queries';
|
||||||
import { Session } from 'next-auth';
|
import { Session } from 'next-auth';
|
||||||
import { Model } from '../models';
|
import { Model } from '../models';
|
||||||
|
|
@ -27,10 +27,10 @@ export const createDocument = ({
|
||||||
}: CreateDocumentProps) =>
|
}: CreateDocumentProps) =>
|
||||||
tool({
|
tool({
|
||||||
description:
|
description:
|
||||||
'Create a document for a writing or content creation activities like image generation. This tool will call other functions that will generate the contents of the document based on the title and kind.',
|
'Create a document for a writing or content creation activities. This tool will call other functions that will generate the contents of the document based on the title and kind.',
|
||||||
parameters: z.object({
|
parameters: z.object({
|
||||||
title: z.string(),
|
title: z.string(),
|
||||||
kind: z.enum(['text', 'code', 'image']),
|
kind: z.enum(['text', 'code', 'image', 'sheet']),
|
||||||
}),
|
}),
|
||||||
execute: async ({ title, kind }) => {
|
execute: async ({ title, kind }) => {
|
||||||
const id = generateUUID();
|
const id = generateUUID();
|
||||||
|
|
@ -123,6 +123,40 @@ export const createDocument = ({
|
||||||
content: image.base64,
|
content: image.base64,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
dataStream.writeData({ type: 'finish', content: '' });
|
||||||
|
} else if (kind === 'sheet') {
|
||||||
|
const { fullStream } = streamObject({
|
||||||
|
model: customModel(model.apiIdentifier),
|
||||||
|
system: sheetPrompt,
|
||||||
|
prompt: title,
|
||||||
|
schema: z.object({
|
||||||
|
csv: z.string().describe('CSV data'),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
for await (const delta of fullStream) {
|
||||||
|
const { type } = delta;
|
||||||
|
|
||||||
|
if (type === 'object') {
|
||||||
|
const { object } = delta;
|
||||||
|
const { csv } = object;
|
||||||
|
|
||||||
|
if (csv) {
|
||||||
|
dataStream.writeData({
|
||||||
|
type: 'sheet-delta',
|
||||||
|
content: csv,
|
||||||
|
});
|
||||||
|
|
||||||
|
draftText = csv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dataStream.writeData({
|
||||||
|
type: 'sheet-delta',
|
||||||
|
content: draftText,
|
||||||
|
});
|
||||||
|
|
||||||
dataStream.writeData({ type: 'finish', content: '' });
|
dataStream.writeData({ type: 'finish', content: '' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -123,6 +123,35 @@ export const updateDocument = ({
|
||||||
content: image.base64,
|
content: image.base64,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
dataStream.writeData({ type: 'finish', content: '' });
|
||||||
|
} else if (document.kind === 'sheet') {
|
||||||
|
const { fullStream } = streamObject({
|
||||||
|
model: customModel(model.apiIdentifier),
|
||||||
|
system: updateDocumentPrompt(currentContent, 'sheet'),
|
||||||
|
prompt: description,
|
||||||
|
schema: z.object({
|
||||||
|
csv: z.string(),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
for await (const delta of fullStream) {
|
||||||
|
const { type } = delta;
|
||||||
|
|
||||||
|
if (type === 'object') {
|
||||||
|
const { object } = delta;
|
||||||
|
const { csv } = object;
|
||||||
|
|
||||||
|
if (csv) {
|
||||||
|
dataStream.writeData({
|
||||||
|
type: 'sheet-delta',
|
||||||
|
content: csv,
|
||||||
|
});
|
||||||
|
|
||||||
|
draftText = csv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dataStream.writeData({ type: 'finish', content: '' });
|
dataStream.writeData({ type: 'finish', content: '' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ export const document = pgTable(
|
||||||
createdAt: timestamp('createdAt').notNull(),
|
createdAt: timestamp('createdAt').notNull(),
|
||||||
title: text('title').notNull(),
|
title: text('title').notNull(),
|
||||||
content: text('content'),
|
content: text('content'),
|
||||||
kind: varchar('text', { enum: ['text', 'code', 'image'] })
|
kind: varchar('text', { enum: ['text', 'code', 'image', 'sheet'] })
|
||||||
.notNull()
|
.notNull()
|
||||||
.default('text'),
|
.default('text'),
|
||||||
userId: uuid('userId')
|
userId: uuid('userId')
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@
|
||||||
"next-auth": "5.0.0-beta.25",
|
"next-auth": "5.0.0-beta.25",
|
||||||
"next-themes": "^0.3.0",
|
"next-themes": "^0.3.0",
|
||||||
"orderedmap": "^2.1.1",
|
"orderedmap": "^2.1.1",
|
||||||
|
"papaparse": "^5.5.2",
|
||||||
"postgres": "^3.4.4",
|
"postgres": "^3.4.4",
|
||||||
"prosemirror-example-setup": "^1.2.3",
|
"prosemirror-example-setup": "^1.2.3",
|
||||||
"prosemirror-inputrules": "^1.4.0",
|
"prosemirror-inputrules": "^1.4.0",
|
||||||
|
|
@ -66,6 +67,7 @@
|
||||||
"prosemirror-state": "^1.4.3",
|
"prosemirror-state": "^1.4.3",
|
||||||
"prosemirror-view": "^1.34.3",
|
"prosemirror-view": "^1.34.3",
|
||||||
"react": "19.0.0-rc-45804af1-20241021",
|
"react": "19.0.0-rc-45804af1-20241021",
|
||||||
|
"react-data-grid": "7.0.0-beta.47",
|
||||||
"react-dom": "19.0.0-rc-45804af1-20241021",
|
"react-dom": "19.0.0-rc-45804af1-20241021",
|
||||||
"react-markdown": "^9.0.1",
|
"react-markdown": "^9.0.1",
|
||||||
"react-resizable-panels": "^2.1.7",
|
"react-resizable-panels": "^2.1.7",
|
||||||
|
|
@ -83,6 +85,7 @@
|
||||||
"@tailwindcss/typography": "^0.5.15",
|
"@tailwindcss/typography": "^0.5.15",
|
||||||
"@types/d3-scale": "^4.0.8",
|
"@types/d3-scale": "^4.0.8",
|
||||||
"@types/node": "^22.8.6",
|
"@types/node": "^22.8.6",
|
||||||
|
"@types/papaparse": "^5.3.15",
|
||||||
"@types/pdf-parse": "^1.1.4",
|
"@types/pdf-parse": "^1.1.4",
|
||||||
"@types/react": "^18",
|
"@types/react": "^18",
|
||||||
"@types/react-dom": "^18",
|
"@types/react-dom": "^18",
|
||||||
|
|
|
||||||
33
pnpm-lock.yaml
generated
33
pnpm-lock.yaml
generated
|
|
@ -122,6 +122,9 @@ importers:
|
||||||
orderedmap:
|
orderedmap:
|
||||||
specifier: ^2.1.1
|
specifier: ^2.1.1
|
||||||
version: 2.1.1
|
version: 2.1.1
|
||||||
|
papaparse:
|
||||||
|
specifier: ^5.5.2
|
||||||
|
version: 5.5.2
|
||||||
postgres:
|
postgres:
|
||||||
specifier: ^3.4.4
|
specifier: ^3.4.4
|
||||||
version: 3.4.5
|
version: 3.4.5
|
||||||
|
|
@ -152,6 +155,9 @@ importers:
|
||||||
react:
|
react:
|
||||||
specifier: 19.0.0-rc-45804af1-20241021
|
specifier: 19.0.0-rc-45804af1-20241021
|
||||||
version: 19.0.0-rc-45804af1-20241021
|
version: 19.0.0-rc-45804af1-20241021
|
||||||
|
react-data-grid:
|
||||||
|
specifier: 7.0.0-beta.47
|
||||||
|
version: 7.0.0-beta.47(react-dom@19.0.0-rc-45804af1-20241021(react@19.0.0-rc-45804af1-20241021))(react@19.0.0-rc-45804af1-20241021)
|
||||||
react-dom:
|
react-dom:
|
||||||
specifier: 19.0.0-rc-45804af1-20241021
|
specifier: 19.0.0-rc-45804af1-20241021
|
||||||
version: 19.0.0-rc-45804af1-20241021(react@19.0.0-rc-45804af1-20241021)
|
version: 19.0.0-rc-45804af1-20241021(react@19.0.0-rc-45804af1-20241021)
|
||||||
|
|
@ -198,6 +204,9 @@ importers:
|
||||||
'@types/node':
|
'@types/node':
|
||||||
specifier: ^22.8.6
|
specifier: ^22.8.6
|
||||||
version: 22.9.0
|
version: 22.9.0
|
||||||
|
'@types/papaparse':
|
||||||
|
specifier: ^5.3.15
|
||||||
|
version: 5.3.15
|
||||||
'@types/pdf-parse':
|
'@types/pdf-parse':
|
||||||
specifier: ^1.1.4
|
specifier: ^1.1.4
|
||||||
version: 1.1.4
|
version: 1.1.4
|
||||||
|
|
@ -1542,6 +1551,9 @@ packages:
|
||||||
'@types/node@22.9.0':
|
'@types/node@22.9.0':
|
||||||
resolution: {integrity: sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==}
|
resolution: {integrity: sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==}
|
||||||
|
|
||||||
|
'@types/papaparse@5.3.15':
|
||||||
|
resolution: {integrity: sha512-JHe6vF6x/8Z85nCX4yFdDslN11d+1pr12E526X8WAfhadOeaOTx5AuIkvDKIBopfvlzpzkdMx4YyvSKCM9oqtw==}
|
||||||
|
|
||||||
'@types/pdf-parse@1.1.4':
|
'@types/pdf-parse@1.1.4':
|
||||||
resolution: {integrity: sha512-+gbBHbNCVGGYw1S9lAIIvrHW47UYOhMIFUsJcMkMrzy1Jf0vulBN3XQIjPgnoOXveMuHnF3b57fXROnY/Or7eg==}
|
resolution: {integrity: sha512-+gbBHbNCVGGYw1S9lAIIvrHW47UYOhMIFUsJcMkMrzy1Jf0vulBN3XQIjPgnoOXveMuHnF3b57fXROnY/Or7eg==}
|
||||||
|
|
||||||
|
|
@ -3016,6 +3028,9 @@ packages:
|
||||||
package-json-from-dist@1.0.1:
|
package-json-from-dist@1.0.1:
|
||||||
resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
|
resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
|
||||||
|
|
||||||
|
papaparse@5.5.2:
|
||||||
|
resolution: {integrity: sha512-PZXg8UuAc4PcVwLosEEDYjPyfWnTEhOrUfdv+3Bx+NuAb+5NhDmXzg5fHWmdCh1mP5p7JAZfFr3IMQfcntNAdA==}
|
||||||
|
|
||||||
parent-module@1.0.1:
|
parent-module@1.0.1:
|
||||||
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
|
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
@ -3229,6 +3244,12 @@ packages:
|
||||||
queue-microtask@1.2.3:
|
queue-microtask@1.2.3:
|
||||||
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
||||||
|
|
||||||
|
react-data-grid@7.0.0-beta.47:
|
||||||
|
resolution: {integrity: sha512-28kjsmwQGD/9RXYC50zn5Zv/SQMhBBoSvG5seq0fM8XXi9TZ0zr9Z5T3YJqLwcEtoNzTOq3y0njkmdujGkIwQQ==}
|
||||||
|
peerDependencies:
|
||||||
|
react: ^18.0 || ^19.0
|
||||||
|
react-dom: ^18.0 || ^19.0
|
||||||
|
|
||||||
react-dom@19.0.0-rc-45804af1-20241021:
|
react-dom@19.0.0-rc-45804af1-20241021:
|
||||||
resolution: {integrity: sha512-8hOckEFO7Vxo+nH/EEddIGdencOFT0/3iJqF3mKrqv71n1xxhYcp0595JbT/DP31G8bHfDcBSMWVhIvyCGWy/A==}
|
resolution: {integrity: sha512-8hOckEFO7Vxo+nH/EEddIGdencOFT0/3iJqF3mKrqv71n1xxhYcp0595JbT/DP31G8bHfDcBSMWVhIvyCGWy/A==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
|
@ -4803,6 +4824,10 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
undici-types: 6.19.8
|
undici-types: 6.19.8
|
||||||
|
|
||||||
|
'@types/papaparse@5.3.15':
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 22.9.0
|
||||||
|
|
||||||
'@types/pdf-parse@1.1.4': {}
|
'@types/pdf-parse@1.1.4': {}
|
||||||
|
|
||||||
'@types/pg@8.11.6':
|
'@types/pg@8.11.6':
|
||||||
|
|
@ -6630,6 +6655,8 @@ snapshots:
|
||||||
|
|
||||||
package-json-from-dist@1.0.1: {}
|
package-json-from-dist@1.0.1: {}
|
||||||
|
|
||||||
|
papaparse@5.5.2: {}
|
||||||
|
|
||||||
parent-module@1.0.1:
|
parent-module@1.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
callsites: 3.1.0
|
callsites: 3.1.0
|
||||||
|
|
@ -6864,6 +6891,12 @@ snapshots:
|
||||||
|
|
||||||
queue-microtask@1.2.3: {}
|
queue-microtask@1.2.3: {}
|
||||||
|
|
||||||
|
react-data-grid@7.0.0-beta.47(react-dom@19.0.0-rc-45804af1-20241021(react@19.0.0-rc-45804af1-20241021))(react@19.0.0-rc-45804af1-20241021):
|
||||||
|
dependencies:
|
||||||
|
clsx: 2.1.1
|
||||||
|
react: 19.0.0-rc-45804af1-20241021
|
||||||
|
react-dom: 19.0.0-rc-45804af1-20241021(react@19.0.0-rc-45804af1-20241021)
|
||||||
|
|
||||||
react-dom@19.0.0-rc-45804af1-20241021(react@19.0.0-rc-45804af1-20241021):
|
react-dom@19.0.0-rc-45804af1-20241021(react@19.0.0-rc-45804af1-20241021):
|
||||||
dependencies:
|
dependencies:
|
||||||
react: 19.0.0-rc-45804af1-20241021
|
react: 19.0.0-rc-45804af1-20241021
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
const config = {
|
const config = {
|
||||||
plugins: {
|
plugins: {
|
||||||
tailwindcss: {},
|
tailwindcss: {},
|
||||||
|
'tailwindcss/nesting': {},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue