Switch to vercel's biome setup (#543)

This commit is contained in:
Jared Palmer 2024-11-15 13:00:15 -05:00 committed by GitHub
parent b8643353c0
commit 43aa1c6e58
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
52 changed files with 414 additions and 201 deletions

View file

@ -17,7 +17,7 @@ export interface LoginActionState {
export const login = async ( export const login = async (
_: LoginActionState, _: LoginActionState,
formData: FormData formData: FormData,
): Promise<LoginActionState> => { ): Promise<LoginActionState> => {
try { try {
const validatedData = authFormSchema.parse({ const validatedData = authFormSchema.parse({
@ -53,7 +53,7 @@ export interface RegisterActionState {
export const register = async ( export const register = async (
_: RegisterActionState, _: RegisterActionState,
formData: FormData formData: FormData,
): Promise<RegisterActionState> => { ): Promise<RegisterActionState> => {
try { try {
const validatedData = authFormSchema.parse({ const validatedData = authFormSchema.parse({

View file

@ -20,12 +20,12 @@ export const {
providers: [ providers: [
Credentials({ Credentials({
credentials: {}, credentials: {},
// biome-ignore lint/suspicious/noExplicitAny: TODO
async authorize({ email, password }: any) { async authorize({ email, password }: any) {
const users = await getUser(email); const users = await getUser(email);
if (users.length === 0) return null; if (users.length === 0) return null;
// biome-ignore lint: Forbidden non-null assertion.
const passwordsMatch = await compare(password, users[0].password!); const passwordsMatch = await compare(password, users[0].password!);
// biome-ignore lint/suspicious/noExplicitAny: TODO if (!passwordsMatch) return null;
return users[0] as any; return users[0] as any;
}, },
}), }),
@ -43,7 +43,6 @@ export const {
token, token,
}: { }: {
session: ExtendedSession; session: ExtendedSession;
// biome-ignore lint/suspicious/noExplicitAny: TODO
token: any; token: any;
}) { }) {
if (session.user) { if (session.user) {

View file

@ -20,7 +20,7 @@ export default function Page() {
login, login,
{ {
status: 'idle', status: 'idle',
} },
); );
useEffect(() => { useEffect(() => {

View file

@ -20,7 +20,7 @@ export default function Page() {
register, register,
{ {
status: 'idle', status: 'idle',
} },
); );
useEffect(() => { useEffect(() => {

View file

@ -1,6 +1,6 @@
'use server'; 'use server';
import { CoreMessage, type CoreUserMessage, generateText } from 'ai'; import { type CoreUserMessage, generateText } from 'ai';
import { cookies } from 'next/headers'; import { cookies } from 'next/headers';
import { customModel } from '@/lib/ai'; import { customModel } from '@/lib/ai';

View file

@ -104,7 +104,7 @@ export async function POST(request: Request) {
}), }),
execute: async ({ latitude, longitude }) => { execute: async ({ latitude, longitude }) => {
const response = await fetch( const response = await fetch(
`https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&current=temperature_2m&hourly=temperature_2m&daily=sunrise,sunset&timezone=auto` `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&current=temperature_2m&hourly=temperature_2m&daily=sunrise,sunset&timezone=auto`,
); );
const weatherData = await response.json(); const weatherData = await response.json();
@ -350,7 +350,7 @@ export async function POST(request: Request) {
content: message.content, content: message.content,
createdAt: new Date(), createdAt: new Date(),
}; };
} },
), ),
}); });
} catch (error) { } catch (error) {

View file

@ -16,7 +16,7 @@ const FileSchema = z.object({
['image/jpeg', 'image/png', 'application/pdf'].includes(file.type), ['image/jpeg', 'image/png', 'application/pdf'].includes(file.type),
{ {
message: 'File type should be JPEG, PNG, or PDF', message: 'File type should be JPEG, PNG, or PDF',
} },
), ),
}); });
@ -65,7 +65,7 @@ export async function POST(request: Request) {
} catch (error) { } catch (error) {
return NextResponse.json( return NextResponse.json(
{ error: 'Failed to process request' }, { error: 'Failed to process request' },
{ status: 500 } { status: 500 },
); );
} }
} }

View file

@ -8,6 +8,7 @@ export async function GET() {
return Response.json('Unauthorized!', { status: 401 }); return Response.json('Unauthorized!', { status: 401 });
} }
// biome-ignore lint: Forbidden non-null assertion.
const chats = await getChatsByUserId({ id: session.user.id! }); const chats = await getChatsByUserId({ id: session.user.id! });
return Response.json(chats); return Response.json(chats);
} }

View file

@ -1,4 +1,4 @@
import { CoreMessage } from 'ai';
import { cookies } from 'next/headers'; import { cookies } from 'next/headers';
import { notFound } from 'next/navigation'; import { notFound } from 'next/navigation';

View file

@ -51,7 +51,6 @@ export default async function RootLayout({
> >
<head> <head>
<script <script
// biome-ignore lint/security/noDangerouslySetInnerHtml: <explanation>
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: THEME_COLOR_SCRIPT, __html: THEME_COLOR_SCRIPT,
}} }}

View file

@ -1,6 +1,5 @@
{ {
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"vcs": { "enabled": false, "clientKind": "git", "useIgnoreFile": true },
"files": { "files": {
"ignoreUnknown": false, "ignoreUnknown": false,
"ignore": [ "ignore": [
@ -13,46 +12,118 @@
".vercel" ".vercel"
] ]
}, },
"vcs": {
"enabled": true,
"clientKind": "git",
"defaultBranch": "main",
"useIgnoreFile": true
},
"formatter": { "formatter": {
"enabled": true, "enabled": true,
"useEditorconfig": true,
"formatWithErrors": false, "formatWithErrors": false,
"indentStyle": "space", "indentStyle": "space",
"indentWidth": 2, "indentWidth": 2,
"lineEnding": "lf", "lineEnding": "lf",
"lineWidth": 80, "lineWidth": 80,
"attributePosition": "auto", "attributePosition": "auto"
"bracketSpacing": true,
"ignore": ["**/pnpm-lock.yaml", "lib/db/drizzle"]
}, },
"organizeImports": { "enabled": true },
"linter": { "linter": {
"enabled": true, "enabled": true,
"rules": { "rules": {
"recommended": true, "recommended": true,
"a11y": { "a11y": {
"noSvgWithoutTitle": "off", "useHtmlLang": "warn",
"useKeyWithClickEvents": "off" "noHeaderScope": "warn",
"useValidAriaRole": {
"level": "warn",
"options": {
"ignoreNonDom": false,
"allowInvalidRoles": ["none", "text"]
}
}, },
"style": { "useSemanticElements": "off",
"noNonNullAssertion": "off" "noSvgWithoutTitle": "off",
"useMediaCaption": "off",
"noAutofocus": "off",
"noBlankTarget": "off"
},
"complexity": {
"noUselessStringConcat": "warn",
"noForEach": "off",
"noUselessSwitchCase": "off",
"noUselessThisAlias": "off"
}, },
"correctness": { "correctness": {
"useExhaustiveDependencies": "off" "noUnusedImports": "warn",
"useArrayLiterals": "warn",
"noNewSymbol": "warn",
"useJsxKeyInIterable": "off",
"useExhaustiveDependencies": "off",
"noUnnecessaryContinue": "off"
},
"security": {
"noDangerouslySetInnerHtml": "off"
},
"style": {
"useFragmentSyntax": "warn",
"noYodaExpression": "warn",
"useDefaultParameterLast": "warn",
"useExponentiationOperator": "off",
"noUnusedTemplateLiteral": "off",
"noUselessElse": "off"
},
"suspicious": {
"noExplicitAny": "off"
},
"nursery": {
"noStaticElementInteractions": "warn",
"noHeadImportInDocument": "warn",
"noDocumentImportInPage": "warn",
"noDuplicateElseIf": "warn",
"noIrregularWhitespace": "warn",
"useValidAutocomplete": "warn"
} }
} }
}, },
"javascript": { "javascript": {
"jsxRuntime": "reactClassic",
"formatter": { "formatter": {
"jsxQuoteStyle": "double", "jsxQuoteStyle": "double",
"quoteProperties": "asNeeded", "quoteProperties": "asNeeded",
"trailingCommas": "es5", "trailingCommas": "all",
"semicolons": "always", "semicolons": "always",
"arrowParentheses": "always", "arrowParentheses": "always",
"bracketSpacing": true,
"bracketSameLine": false, "bracketSameLine": false,
"quoteStyle": "single", "quoteStyle": "single",
"attributePosition": "auto", "attributePosition": "auto"
"bracketSpacing": true }
},
"json": {
"formatter": {
"enabled": true,
"trailingCommas": "none"
},
"parser": {
"allowComments": true,
"allowTrailingCommas": false
}
},
"css": {
"formatter": { "enabled": false },
"linter": { "enabled": false }
},
"organizeImports": { "enabled": false },
"overrides": [
{
"include": ["playwright/**", "tooling/playwright/src/test/test.ts"],
"linter": {
"rules": {
"correctness": {
"noEmptyPattern": "off"
} }
} }
} }
}
]
}

131
biome.jsonc Normal file
View file

@ -0,0 +1,131 @@
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"files": {
"ignoreUnknown": false,
"ignore": [
"**/pnpm-lock.yaml",
"lib/db/migrations",
"lib/editor/react-renderer.tsx",
"node_modules",
".next",
"public",
".vercel"
]
},
"vcs": {
"enabled": true,
"clientKind": "git",
"defaultBranch": "main",
"useIgnoreFile": true
},
"formatter": {
"enabled": true,
"formatWithErrors": false,
"indentStyle": "space",
"indentWidth": 2,
"lineEnding": "lf",
"lineWidth": 80,
"attributePosition": "auto"
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"a11y": {
"useHtmlLang": "warn", // Not in recommended ruleset, turning on manually
"noHeaderScope": "warn", // Not in recommended ruleset, turning on manually
"useValidAriaRole": {
"level": "warn",
"options": {
"ignoreNonDom": false,
"allowInvalidRoles": ["none", "text"]
}
},
"useSemanticElements": "off", // Rule is buggy, revisit later
"noSvgWithoutTitle": "off", // We do not intend to adhere to this rule
"useMediaCaption": "off", // We would need a cultural change to turn this on
"noAutofocus": "off", // We're highly intentional about when we use autofocus
"noBlankTarget": "off" // Covered by Conformance
},
"complexity": {
"noUselessStringConcat": "warn", // Not in recommended ruleset, turning on manually
"noForEach": "off", // forEach is too familiar to ban
"noUselessSwitchCase": "off", // Turned off due to developer preferences
"noUselessThisAlias": "off" // Turned off due to developer preferences
},
"correctness": {
"noUnusedImports": "warn", // Not in recommended ruleset, turning on manually
"useArrayLiterals": "warn", // Not in recommended ruleset, turning on manually
"noNewSymbol": "warn", // Not in recommended ruleset, turning on manually
"useJsxKeyInIterable": "off", // Rule is buggy, revisit later
"useExhaustiveDependencies": "off", // Community feedback on this rule has been poor, we will continue with ESLint
"noUnnecessaryContinue": "off" // Turned off due to developer preferences
},
"security": {
"noDangerouslySetInnerHtml": "off" // Covered by Conformance
},
"style": {
"useFragmentSyntax": "warn", // Not in recommended ruleset, turning on manually
"noYodaExpression": "warn", // Not in recommended ruleset, turning on manually
"useDefaultParameterLast": "warn", // Not in recommended ruleset, turning on manually
"useExponentiationOperator": "off", // Obscure and arguably not easily readable
"noUnusedTemplateLiteral": "off", // Stylistic opinion
"noUselessElse": "off" // Stylistic opinion
},
"suspicious": {
"noExplicitAny": "off" // We trust Vercelians to use any only when necessary
},
"nursery": {
"noStaticElementInteractions": "warn",
"noHeadImportInDocument": "warn",
"noDocumentImportInPage": "warn",
"noDuplicateElseIf": "warn",
"noIrregularWhitespace": "warn",
"useValidAutocomplete": "warn"
}
}
},
"javascript": {
"jsxRuntime": "reactClassic",
"formatter": {
"jsxQuoteStyle": "double",
"quoteProperties": "asNeeded",
"trailingCommas": "all",
"semicolons": "always",
"arrowParentheses": "always",
"bracketSpacing": true,
"bracketSameLine": false,
"quoteStyle": "single",
"attributePosition": "auto"
}
},
"json": {
"formatter": {
"enabled": true,
"trailingCommas": "none"
},
"parser": {
"allowComments": true,
"allowTrailingCommas": false
}
},
"css": {
"formatter": { "enabled": false },
"linter": { "enabled": false }
},
"organizeImports": { "enabled": false },
"overrides": [
// Playwright requires an object destructure, even if empty
// https://github.com/microsoft/playwright/issues/30007
{
"include": ["playwright/**"],
"linter": {
"rules": {
"correctness": {
"noEmptyPattern": "off"
}
}
}
}
]
}

View file

@ -18,6 +18,7 @@ import {
useSidebar, useSidebar,
} from '@/components/ui/sidebar'; } from '@/components/ui/sidebar';
import { BetterTooltip } from '@/components/ui/tooltip'; import { BetterTooltip } from '@/components/ui/tooltip';
import Link from 'next/link';
export function AppSidebar({ user }: { user: User | undefined }) { export function AppSidebar({ user }: { user: User | undefined }) {
const router = useRouter(); const router = useRouter();
@ -28,21 +29,21 @@ export function AppSidebar({ user }: { user: User | undefined }) {
<SidebarHeader> <SidebarHeader>
<SidebarMenu> <SidebarMenu>
<div className="flex flex-row justify-between items-center"> <div className="flex flex-row justify-between items-center">
<div <Link
href="/"
onClick={() => { onClick={() => {
setOpenMobile(false); setOpenMobile(false);
router.push('/');
router.refresh();
}} }}
className="flex flex-row gap-3 items-center" className="flex flex-row gap-3 items-center"
> >
<span className="text-lg font-semibold px-2 hover:bg-muted rounded-md cursor-pointer"> <span className="text-lg font-semibold px-2 hover:bg-muted rounded-md cursor-pointer">
Chatbot Chatbot
</span> </span>
</div> </Link>
<BetterTooltip content="New Chat" align="start"> <BetterTooltip content="New Chat" align="start">
<Button <Button
variant="ghost" variant="ghost"
type="button"
className="p-2 h-fit" className="p-2 h-fit"
onClick={() => { onClick={() => {
setOpenMobile(false); setOpenMobile(false);

View file

@ -23,7 +23,7 @@ export function PureBlockStreamHandler({
function areEqual( function areEqual(
prevProps: BlockStreamHandlerProps, prevProps: BlockStreamHandlerProps,
nextProps: BlockStreamHandlerProps nextProps: BlockStreamHandlerProps,
) { ) {
if (!prevProps.streamingData && !nextProps.streamingData) { if (!prevProps.streamingData && !nextProps.streamingData) {
return true; return true;

View file

@ -80,13 +80,13 @@ export function Block({
votes: Array<Vote> | undefined; votes: Array<Vote> | undefined;
append: ( append: (
message: Message | CreateMessage, message: Message | CreateMessage,
chatRequestOptions?: ChatRequestOptions chatRequestOptions?: ChatRequestOptions,
) => Promise<string | null | undefined>; ) => Promise<string | null | undefined>;
handleSubmit: ( handleSubmit: (
event?: { event?: {
preventDefault?: () => void; preventDefault?: () => void;
}, },
chatRequestOptions?: ChatRequestOptions chatRequestOptions?: ChatRequestOptions,
) => void; ) => void;
}) { }) {
const [messagesContainerRef, messagesEndRef] = const [messagesContainerRef, messagesEndRef] =
@ -100,7 +100,7 @@ export function Block({
block && block.status !== 'streaming' block && block.status !== 'streaming'
? `/api/document?id=${block.documentId}` ? `/api/document?id=${block.documentId}`
: null, : null,
fetcher fetcher,
); );
const { data: suggestions } = useSWR<Array<Suggestion>>( const { data: suggestions } = useSWR<Array<Suggestion>>(
@ -110,7 +110,7 @@ export function Block({
fetcher, fetcher,
{ {
dedupingInterval: 5000, dedupingInterval: 5000,
} },
); );
const [mode, setMode] = useState<'edit' | 'diff'>('edit'); const [mode, setMode] = useState<'edit' | 'diff'>('edit');
@ -176,15 +176,15 @@ export function Block({
} }
return currentDocuments; return currentDocuments;
}, },
{ revalidate: false } { revalidate: false },
); );
}, },
[block, mutate] [block, mutate],
); );
const debouncedHandleContentChange = useDebounceCallback( const debouncedHandleContentChange = useDebounceCallback(
handleContentChange, handleContentChange,
2000 2000,
); );
const saveContent = useCallback( const saveContent = useCallback(
@ -199,7 +199,7 @@ export function Block({
} }
} }
}, },
[document, debouncedHandleContentChange, handleContentChange] [document, debouncedHandleContentChange, handleContentChange],
); );
function getDocumentContentById(index: number) { function getDocumentContentById(index: number) {
@ -430,7 +430,7 @@ export function Block({
new Date(), new Date(),
{ {
addSuffix: true, addSuffix: true,
} },
)}`} )}`}
</div> </div>
) : ( ) : (
@ -496,7 +496,7 @@ export function Block({
'p-2 h-fit !pointer-events-auto dark:hover:bg-zinc-700', 'p-2 h-fit !pointer-events-auto dark:hover:bg-zinc-700',
{ {
'bg-muted': mode === 'diff', 'bg-muted': mode === 'diff',
} },
)} )}
onClick={() => { onClick={() => {
handleVersionChange('toggle'); handleVersionChange('toggle');

View file

@ -66,7 +66,7 @@ export function Chat({
const { data: votes } = useSWR<Array<Vote>>( const { data: votes } = useSWR<Array<Vote>>(
`/api/vote?chatId=${id}`, `/api/vote?chatId=${id}`,
fetcher fetcher,
); );
const [messagesContainerRef, messagesEndRef] = const [messagesContainerRef, messagesEndRef] =

View file

@ -60,10 +60,10 @@ export const DiffView = ({ oldContent, newContent }: DiffEditorProps) => {
const parser = DOMParser.fromSchema(diffSchema); const parser = DOMParser.fromSchema(diffSchema);
const oldHtmlContent = renderToString( const oldHtmlContent = renderToString(
<ReactMarkdown>{oldContent}</ReactMarkdown> <ReactMarkdown>{oldContent}</ReactMarkdown>,
); );
const newHtmlContent = renderToString( const newHtmlContent = renderToString(
<ReactMarkdown>{newContent}</ReactMarkdown> <ReactMarkdown>{newContent}</ReactMarkdown>,
); );
const oldContainer = document.createElement('div'); const oldContainer = document.createElement('div');

View file

@ -30,7 +30,8 @@ export function DocumentToolResult({
setBlock, setBlock,
}: DocumentToolResultProps) { }: DocumentToolResultProps) {
return ( return (
<div <button
type="button"
className="bg-background cursor-pointer border py-2 px-3 rounded-xl w-fit flex flex-row gap-3 items-start" className="bg-background cursor-pointer border py-2 px-3 rounded-xl w-fit flex flex-row gap-3 items-start"
onClick={(event) => { onClick={(event) => {
const rect = event.currentTarget.getBoundingClientRect(); const rect = event.currentTarget.getBoundingClientRect();
@ -64,7 +65,7 @@ export function DocumentToolResult({
<div className=""> <div className="">
{getActionText(type)} {result.title} {getActionText(type)} {result.title}
</div> </div>
</div> </button>
); );
} }

View file

@ -89,7 +89,7 @@ function PureEditor({
useEffect(() => { useEffect(() => {
if (editorRef.current && content) { if (editorRef.current && content) {
const currentContent = buildContentFromDocument( const currentContent = buildContentFromDocument(
editorRef.current.state.doc editorRef.current.state.doc,
); );
if (status === 'streaming') { if (status === 'streaming') {
@ -98,7 +98,7 @@ function PureEditor({
const transaction = editorRef.current.state.tr.replaceWith( const transaction = editorRef.current.state.tr.replaceWith(
0, 0,
editorRef.current.state.doc.content.size, editorRef.current.state.doc.content.size,
newDocument.content newDocument.content,
); );
transaction.setMeta('no-save', true); transaction.setMeta('no-save', true);
@ -112,7 +112,7 @@ function PureEditor({
const transaction = editorRef.current.state.tr.replaceWith( const transaction = editorRef.current.state.tr.replaceWith(
0, 0,
editorRef.current.state.doc.content.size, editorRef.current.state.doc.content.size,
newDocument.content newDocument.content,
); );
transaction.setMeta('no-save', true); transaction.setMeta('no-save', true);
@ -125,14 +125,14 @@ function PureEditor({
if (editorRef.current?.state.doc && content) { if (editorRef.current?.state.doc && content) {
const projectedSuggestions = projectWithPositions( const projectedSuggestions = projectWithPositions(
editorRef.current.state.doc, editorRef.current.state.doc,
suggestions suggestions,
).filter( ).filter(
(suggestion) => suggestion.selectionStart && suggestion.selectionEnd (suggestion) => suggestion.selectionStart && suggestion.selectionEnd,
); );
const decorations = createDecorations( const decorations = createDecorations(
projectedSuggestions, projectedSuggestions,
editorRef.current editorRef.current,
); );
const transaction = editorRef.current.state.tr; const transaction = editorRef.current.state.tr;

View file

@ -119,5 +119,5 @@ const NonMemoizedMarkdown = ({ children }: { children: string }) => {
export const Markdown = memo( export const Markdown = memo(
NonMemoizedMarkdown, NonMemoizedMarkdown,
(prevProps, nextProps) => prevProps.children === nextProps.children (prevProps, nextProps) => prevProps.children === nextProps.children,
); );

View file

@ -80,7 +80,7 @@ export function MessageActions({
if (!currentVotes) return []; if (!currentVotes) return [];
const votesWithoutCurrent = currentVotes.filter( const votesWithoutCurrent = currentVotes.filter(
(vote) => vote.messageId !== message.id (vote) => vote.messageId !== message.id,
); );
return [ return [
@ -92,7 +92,7 @@ export function MessageActions({
}, },
]; ];
}, },
{ revalidate: false } { revalidate: false },
); );
return 'Upvoted Response!'; return 'Upvoted Response!';
@ -134,7 +134,7 @@ export function MessageActions({
if (!currentVotes) return []; if (!currentVotes) return [];
const votesWithoutCurrent = currentVotes.filter( const votesWithoutCurrent = currentVotes.filter(
(vote) => vote.messageId !== message.id (vote) => vote.messageId !== message.id,
); );
return [ return [
@ -146,7 +146,7 @@ export function MessageActions({
}, },
]; ];
}, },
{ revalidate: false } { revalidate: false },
); );
return 'Downvoted Response!'; return 'Downvoted Response!';

View file

@ -39,7 +39,7 @@ export const PreviewMessage = ({
> >
<div <div
className={cx( className={cx(
'group-data-[role=user]/message:bg-primary group-data-[role=user]/message:text-primary-foreground flex gap-4 group-data-[role=user]/message:px-3 w-full group-data-[role=user]/message:w-fit group-data-[role=user]/message:ml-auto group-data-[role=user]/message:max-w-2xl group-data-[role=user]/message:py-2 rounded-xl' 'group-data-[role=user]/message:bg-primary group-data-[role=user]/message:text-primary-foreground flex gap-4 group-data-[role=user]/message:px-3 w-full group-data-[role=user]/message:w-fit group-data-[role=user]/message:ml-auto group-data-[role=user]/message:max-w-2xl group-data-[role=user]/message:py-2 rounded-xl',
)} )}
> >
{message.role === 'assistant' && ( {message.role === 'assistant' && (
@ -158,7 +158,7 @@ export const ThinkingMessage = () => {
'flex gap-4 group-data-[role=user]/message:px-3 w-full group-data-[role=user]/message:w-fit group-data-[role=user]/message:ml-auto group-data-[role=user]/message:max-w-2xl group-data-[role=user]/message:py-2 rounded-xl', 'flex gap-4 group-data-[role=user]/message:px-3 w-full group-data-[role=user]/message:w-fit group-data-[role=user]/message:ml-auto group-data-[role=user]/message:max-w-2xl group-data-[role=user]/message:py-2 rounded-xl',
{ {
'group-data-[role=user]/message:bg-muted': true, 'group-data-[role=user]/message:bg-muted': true,
} },
)} )}
> >
<div className="size-8 flex items-center rounded-full justify-center ring-1 shrink-0 ring-border"> <div className="size-8 flex items-center rounded-full justify-center ring-1 shrink-0 ring-border">

View file

@ -27,7 +27,7 @@ export function ModelSelector({
const selectModel = useMemo( const selectModel = useMemo(
() => models.find((model) => model.id === optimisticModelId), () => models.find((model) => model.id === optimisticModelId),
[optimisticModelId] [optimisticModelId],
); );
return ( return (
@ -36,7 +36,7 @@ export function ModelSelector({
asChild asChild
className={cn( className={cn(
'w-fit data-[state=open]:bg-accent data-[state=open]:text-accent-foreground', 'w-fit data-[state=open]:bg-accent data-[state=open]:text-accent-foreground',
className className,
)} )}
> >
<Button variant="outline" className="md:px-2 md:h-[34px]"> <Button variant="outline" className="md:px-2 md:h-[34px]">

View file

@ -66,13 +66,13 @@ export function MultimodalInput({
setMessages: Dispatch<SetStateAction<Array<Message>>>; setMessages: Dispatch<SetStateAction<Array<Message>>>;
append: ( append: (
message: Message | CreateMessage, message: Message | CreateMessage,
chatRequestOptions?: ChatRequestOptions chatRequestOptions?: ChatRequestOptions,
) => Promise<string | null | undefined>; ) => Promise<string | null | undefined>;
handleSubmit: ( handleSubmit: (
event?: { event?: {
preventDefault?: () => void; preventDefault?: () => void;
}, },
chatRequestOptions?: ChatRequestOptions chatRequestOptions?: ChatRequestOptions,
) => void; ) => void;
className?: string; className?: string;
}) { }) {
@ -94,7 +94,7 @@ export function MultimodalInput({
const [localStorageInput, setLocalStorageInput] = useLocalStorage( const [localStorageInput, setLocalStorageInput] = useLocalStorage(
'input', 'input',
'' '',
); );
useEffect(() => { useEffect(() => {
@ -180,7 +180,7 @@ export function MultimodalInput({
const uploadPromises = files.map((file) => uploadFile(file)); const uploadPromises = files.map((file) => uploadFile(file));
const uploadedAttachments = await Promise.all(uploadPromises); const uploadedAttachments = await Promise.all(uploadPromises);
const successfullyUploadedAttachments = uploadedAttachments.filter( const successfullyUploadedAttachments = uploadedAttachments.filter(
(attachment) => attachment !== undefined (attachment) => attachment !== undefined,
); );
setAttachments((currentAttachments) => [ setAttachments((currentAttachments) => [
@ -193,7 +193,7 @@ export function MultimodalInput({
setUploadQueue([]); setUploadQueue([]);
} }
}, },
[setAttachments] [setAttachments],
); );
return ( return (
@ -269,7 +269,7 @@ export function MultimodalInput({
onChange={handleInput} onChange={handleInput}
className={cx( className={cx(
'min-h-[24px] max-h-[calc(75dvh)] overflow-hidden resize-none rounded-xl text-base bg-muted', 'min-h-[24px] max-h-[calc(75dvh)] overflow-hidden resize-none rounded-xl text-base bg-muted',
className className,
)} )}
rows={3} rows={3}
autoFocus autoFocus

View file

@ -213,7 +213,7 @@ export function SidebarHistory({ user }: { user: User | undefined }) {
lastWeek: [], lastWeek: [],
lastMonth: [], lastMonth: [],
older: [], older: [],
} as GroupedChats } as GroupedChats,
); );
}; };

View file

@ -2,7 +2,6 @@ import type { ComponentProps } from 'react';
import { type SidebarTrigger, useSidebar } from '@/components/ui/sidebar'; import { type SidebarTrigger, useSidebar } from '@/components/ui/sidebar';
import { BetterTooltip } from '@/components/ui/tooltip'; import { BetterTooltip } from '@/components/ui/tooltip';
import { cn } from '@/lib/utils';
import { SidebarLeftIcon } from './icons'; import { SidebarLeftIcon } from './icons';
import { Button } from './ui/button'; import { Button } from './ui/button';

View file

@ -46,14 +46,15 @@ export const Suggestion = ({
<div className="size-4 bg-muted-foreground/25 rounded-full" /> <div className="size-4 bg-muted-foreground/25 rounded-full" />
<div className="font-medium">Assistant</div> <div className="font-medium">Assistant</div>
</div> </div>
<div <button
type="button"
className="text-xs text-gray-500 cursor-pointer" className="text-xs text-gray-500 cursor-pointer"
onClick={() => { onClick={() => {
setIsExpanded(false); setIsExpanded(false);
}} }}
> >
<CrossIcon size={12} /> <CrossIcon size={12} />
</div> </button>
</div> </div>
<div>{suggestion.description}</div> <div>{suggestion.description}</div>
<Button <Button

View file

@ -32,7 +32,6 @@ import {
StopIcon, StopIcon,
SummarizeIcon, SummarizeIcon,
} from './icons'; } from './icons';
import { Button } from './ui/button';
type ToolProps = { type ToolProps = {
type: 'final-polish' | 'request-suggestions' | 'adjust-reading-level'; type: 'final-polish' | 'request-suggestions' | 'adjust-reading-level';
@ -45,7 +44,7 @@ type ToolProps = {
isAnimating: boolean; isAnimating: boolean;
append: ( append: (
message: Message | CreateMessage, message: Message | CreateMessage,
chatRequestOptions?: ChatRequestOptions chatRequestOptions?: ChatRequestOptions,
) => Promise<string | null | undefined>; ) => Promise<string | null | undefined>;
}; };
@ -159,7 +158,7 @@ const ReadingLevelSelector = ({
isAnimating: boolean; isAnimating: boolean;
append: ( append: (
message: Message | CreateMessage, message: Message | CreateMessage,
chatRequestOptions?: ChatRequestOptions chatRequestOptions?: ChatRequestOptions,
) => Promise<string | null | undefined>; ) => Promise<string | null | undefined>;
}) => { }) => {
const LEVELS = [ const LEVELS = [
@ -212,7 +211,7 @@ const ReadingLevelSelector = ({
{ {
'bg-primary text-primary-foreground': currentLevel !== 2, 'bg-primary text-primary-foreground': currentLevel !== 2,
'bg-background text-foreground': currentLevel === 2, 'bg-background text-foreground': currentLevel === 2,
} },
)} )}
style={{ y }} style={{ y }}
drag="y" drag="y"
@ -272,7 +271,7 @@ export const Tools = ({
setSelectedTool: Dispatch<SetStateAction<string | null>>; setSelectedTool: Dispatch<SetStateAction<string | null>>;
append: ( append: (
message: Message | CreateMessage, message: Message | CreateMessage,
chatRequestOptions?: ChatRequestOptions chatRequestOptions?: ChatRequestOptions,
) => Promise<string | null | undefined>; ) => Promise<string | null | undefined>;
isAnimating: boolean; isAnimating: boolean;
setIsToolbarVisible: Dispatch<SetStateAction<boolean>>; setIsToolbarVisible: Dispatch<SetStateAction<boolean>>;
@ -338,7 +337,7 @@ export const Toolbar = ({
isLoading: boolean; isLoading: boolean;
append: ( append: (
message: Message | CreateMessage, message: Message | CreateMessage,
chatRequestOptions?: ChatRequestOptions chatRequestOptions?: ChatRequestOptions,
) => Promise<string | null | undefined>; ) => Promise<string | null | undefined>;
stop: () => void; stop: () => void;
setMessages: Dispatch<SetStateAction<Message[]>>; setMessages: Dispatch<SetStateAction<Message[]>>;

View file

@ -19,7 +19,7 @@ const AlertDialogOverlay = React.forwardRef<
<AlertDialogPrimitive.Overlay <AlertDialogPrimitive.Overlay
className={cn( className={cn(
'fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0', 'fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
className className,
)} )}
{...props} {...props}
ref={ref} ref={ref}
@ -37,7 +37,7 @@ const AlertDialogContent = React.forwardRef<
ref={ref} ref={ref}
className={cn( className={cn(
'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg', 'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
className className,
)} )}
{...props} {...props}
/> />
@ -52,7 +52,7 @@ const AlertDialogHeader = ({
<div <div
className={cn( className={cn(
'flex flex-col space-y-2 text-center sm:text-left', 'flex flex-col space-y-2 text-center sm:text-left',
className className,
)} )}
{...props} {...props}
/> />
@ -66,7 +66,7 @@ const AlertDialogFooter = ({
<div <div
className={cn( className={cn(
'flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2', 'flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2',
className className,
)} )}
{...props} {...props}
/> />
@ -119,7 +119,7 @@ const AlertDialogCancel = React.forwardRef<
className={cn( className={cn(
buttonVariants({ variant: 'outline' }), buttonVariants({ variant: 'outline' }),
'mt-2 sm:mt-0', 'mt-2 sm:mt-0',
className className,
)} )}
{...props} {...props}
/> />

View file

@ -30,7 +30,7 @@ const buttonVariants = cva(
variant: 'default', variant: 'default',
size: 'default', size: 'default',
}, },
} },
); );
export interface ButtonProps export interface ButtonProps
@ -49,7 +49,7 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
{...props} {...props}
/> />
); );
} },
); );
Button.displayName = 'Button'; Button.displayName = 'Button';

View file

@ -10,7 +10,7 @@ const Card = React.forwardRef<
ref={ref} ref={ref}
className={cn( className={cn(
'rounded-lg border bg-card text-card-foreground shadow-sm', 'rounded-lg border bg-card text-card-foreground shadow-sm',
className className,
)} )}
{...props} {...props}
/> />
@ -37,7 +37,7 @@ const CardTitle = React.forwardRef<
ref={ref} ref={ref}
className={cn( className={cn(
'text-2xl font-semibold leading-none tracking-tight', 'text-2xl font-semibold leading-none tracking-tight',
className className,
)} )}
{...props} {...props}
/> />

View file

@ -29,7 +29,7 @@ const DropdownMenuSubTrigger = React.forwardRef<
className={cn( className={cn(
'flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent', 'flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent',
inset && 'pl-8', inset && 'pl-8',
className className,
)} )}
{...props} {...props}
> >
@ -48,7 +48,7 @@ const DropdownMenuSubContent = React.forwardRef<
ref={ref} ref={ref}
className={cn( className={cn(
'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2', 'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
className className,
)} )}
{...props} {...props}
/> />
@ -66,7 +66,7 @@ const DropdownMenuContent = React.forwardRef<
sideOffset={sideOffset} sideOffset={sideOffset}
className={cn( className={cn(
'z-50 min-w-[8rem] overflow-hidden rounded-lg border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2', 'z-50 min-w-[8rem] overflow-hidden rounded-lg border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
className className,
)} )}
{...props} {...props}
/> />
@ -85,7 +85,7 @@ const DropdownMenuItem = React.forwardRef<
className={cn( className={cn(
'relative flex gap-2 cursor-default select-none items-center rounded-md px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0', 'relative flex gap-2 cursor-default select-none items-center rounded-md px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
inset && 'pl-8', inset && 'pl-8',
className className,
)} )}
{...props} {...props}
/> />
@ -100,7 +100,7 @@ const DropdownMenuCheckboxItem = React.forwardRef<
ref={ref} ref={ref}
className={cn( className={cn(
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50', 'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
className className,
)} )}
checked={checked} checked={checked}
{...props} {...props}
@ -124,7 +124,7 @@ const DropdownMenuRadioItem = React.forwardRef<
ref={ref} ref={ref}
className={cn( className={cn(
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50', 'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
className className,
)} )}
{...props} {...props}
> >
@ -149,7 +149,7 @@ const DropdownMenuLabel = React.forwardRef<
className={cn( className={cn(
'px-2 py-1.5 text-sm font-semibold', 'px-2 py-1.5 text-sm font-semibold',
inset && 'pl-8', inset && 'pl-8',
className className,
)} )}
{...props} {...props}
/> />

View file

@ -12,13 +12,13 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
type={type} type={type}
className={cn( className={cn(
'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50', 'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
className className,
)} )}
ref={ref} ref={ref}
{...props} {...props}
/> />
); );
} },
); );
Input.displayName = 'Input'; Input.displayName = 'Input';

View file

@ -7,7 +7,7 @@ import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
const labelVariants = cva( const labelVariants = cva(
'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70' 'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
); );
const Label = React.forwardRef< const Label = React.forwardRef<

View file

@ -20,7 +20,7 @@ const SelectTrigger = React.forwardRef<
ref={ref} ref={ref}
className={cn( className={cn(
'flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1', 'flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1',
className className,
)} )}
{...props} {...props}
> >
@ -40,7 +40,7 @@ const SelectScrollUpButton = React.forwardRef<
ref={ref} ref={ref}
className={cn( className={cn(
'flex cursor-default items-center justify-center py-1', 'flex cursor-default items-center justify-center py-1',
className className,
)} )}
{...props} {...props}
> >
@ -57,7 +57,7 @@ const SelectScrollDownButton = React.forwardRef<
ref={ref} ref={ref}
className={cn( className={cn(
'flex cursor-default items-center justify-center py-1', 'flex cursor-default items-center justify-center py-1',
className className,
)} )}
{...props} {...props}
> >
@ -78,7 +78,7 @@ const SelectContent = React.forwardRef<
'relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2', 'relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
position === 'popper' && position === 'popper' &&
'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1', 'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
className className,
)} )}
position={position} position={position}
{...props} {...props}
@ -88,7 +88,7 @@ const SelectContent = React.forwardRef<
className={cn( className={cn(
'p-1', 'p-1',
position === 'popper' && position === 'popper' &&
'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]' 'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]',
)} )}
> >
{children} {children}
@ -119,7 +119,7 @@ const SelectItem = React.forwardRef<
ref={ref} ref={ref}
className={cn( className={cn(
'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50', 'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
className className,
)} )}
{...props} {...props}
> >

View file

@ -11,7 +11,7 @@ const Separator = React.forwardRef<
>( >(
( (
{ className, orientation = 'horizontal', decorative = true, ...props }, { className, orientation = 'horizontal', decorative = true, ...props },
ref ref,
) => ( ) => (
<SeparatorPrimitive.Root <SeparatorPrimitive.Root
ref={ref} ref={ref}
@ -20,11 +20,11 @@ const Separator = React.forwardRef<
className={cn( className={cn(
'shrink-0 bg-border', 'shrink-0 bg-border',
orientation === 'horizontal' ? 'h-[1px] w-full' : 'h-full w-[1px]', orientation === 'horizontal' ? 'h-[1px] w-full' : 'h-full w-[1px]',
className className,
)} )}
{...props} {...props}
/> />
) ),
); );
Separator.displayName = SeparatorPrimitive.Root.displayName; Separator.displayName = SeparatorPrimitive.Root.displayName;

View file

@ -22,7 +22,7 @@ const SheetOverlay = React.forwardRef<
<SheetPrimitive.Overlay <SheetPrimitive.Overlay
className={cn( className={cn(
'fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0', 'fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
className className,
)} )}
{...props} {...props}
ref={ref} ref={ref}
@ -46,7 +46,7 @@ const sheetVariants = cva(
defaultVariants: { defaultVariants: {
side: 'right', side: 'right',
}, },
} },
); );
interface SheetContentProps interface SheetContentProps
@ -81,7 +81,7 @@ const SheetHeader = ({
<div <div
className={cn( className={cn(
'flex flex-col space-y-2 text-center sm:text-left', 'flex flex-col space-y-2 text-center sm:text-left',
className className,
)} )}
{...props} {...props}
/> />
@ -95,7 +95,7 @@ const SheetFooter = ({
<div <div
className={cn( className={cn(
'flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2', 'flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2',
className className,
)} )}
{...props} {...props}
/> />

View file

@ -70,7 +70,7 @@ const SidebarProvider = React.forwardRef<
children, children,
...props ...props
}, },
ref ref,
) => { ) => {
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const [openMobile, setOpenMobile] = React.useState(false); const [openMobile, setOpenMobile] = React.useState(false);
@ -91,7 +91,7 @@ const SidebarProvider = React.forwardRef<
// This sets the cookie to keep the sidebar state. // This sets the cookie to keep the sidebar state.
document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`; document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
}, },
[setOpenProp, open] [setOpenProp, open],
); );
// Helper to toggle the sidebar. // Helper to toggle the sidebar.
@ -131,7 +131,15 @@ const SidebarProvider = React.forwardRef<
setOpenMobile, setOpenMobile,
toggleSidebar, toggleSidebar,
}), }),
[state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar] [
state,
open,
setOpen,
isMobile,
openMobile,
setOpenMobile,
toggleSidebar,
],
); );
return ( return (
@ -147,7 +155,7 @@ const SidebarProvider = React.forwardRef<
} }
className={cn( className={cn(
'group/sidebar-wrapper flex min-h-svh w-full has-[[data-variant=inset]]:bg-sidebar', 'group/sidebar-wrapper flex min-h-svh w-full has-[[data-variant=inset]]:bg-sidebar',
className className,
)} )}
data-state={state} data-state={state}
ref={ref} ref={ref}
@ -158,7 +166,7 @@ const SidebarProvider = React.forwardRef<
</TooltipProvider> </TooltipProvider>
</SidebarContext.Provider> </SidebarContext.Provider>
); );
} },
); );
SidebarProvider.displayName = 'SidebarProvider'; SidebarProvider.displayName = 'SidebarProvider';
@ -179,7 +187,7 @@ const Sidebar = React.forwardRef<
children, children,
...props ...props
}, },
ref ref,
) => { ) => {
const { isMobile, state, openMobile, setOpenMobile } = useSidebar(); const { isMobile, state, openMobile, setOpenMobile } = useSidebar();
@ -188,7 +196,7 @@ const Sidebar = React.forwardRef<
<div <div
className={cn( className={cn(
'flex h-full w-[--sidebar-width] flex-col bg-sidebar text-sidebar-foreground', 'flex h-full w-[--sidebar-width] flex-col bg-sidebar text-sidebar-foreground',
className className,
)} )}
ref={ref} ref={ref}
{...props} {...props}
@ -239,7 +247,7 @@ const Sidebar = React.forwardRef<
'group-data-[side=right]:rotate-180', 'group-data-[side=right]:rotate-180',
variant === 'floating' || variant === 'inset' variant === 'floating' || variant === 'inset'
? 'group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4))]' ? 'group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4))]'
: 'group-data-[collapsible=icon]:w-[--sidebar-width-icon]' : 'group-data-[collapsible=icon]:w-[--sidebar-width-icon]',
)} )}
/> />
<div <div
@ -252,7 +260,7 @@ const Sidebar = React.forwardRef<
variant === 'floating' || variant === 'inset' variant === 'floating' || variant === 'inset'
? 'p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4)_+2px)]' ? 'p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4)_+2px)]'
: 'group-data-[collapsible=icon]:w-[--sidebar-width-icon] group-data-[side=left]:border-r group-data-[side=right]:border-l', : 'group-data-[collapsible=icon]:w-[--sidebar-width-icon] group-data-[side=left]:border-r group-data-[side=right]:border-l',
className className,
)} )}
{...props} {...props}
> >
@ -265,7 +273,7 @@ const Sidebar = React.forwardRef<
</div> </div>
</div> </div>
); );
} },
); );
Sidebar.displayName = 'Sidebar'; Sidebar.displayName = 'Sidebar';
@ -315,7 +323,7 @@ const SidebarRail = React.forwardRef<
'group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full group-data-[collapsible=offcanvas]:hover:bg-sidebar', 'group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full group-data-[collapsible=offcanvas]:hover:bg-sidebar',
'[[data-side=left][data-collapsible=offcanvas]_&]:-right-2', '[[data-side=left][data-collapsible=offcanvas]_&]:-right-2',
'[[data-side=right][data-collapsible=offcanvas]_&]:-left-2', '[[data-side=right][data-collapsible=offcanvas]_&]:-left-2',
className className,
)} )}
{...props} {...props}
/> />
@ -333,7 +341,7 @@ const SidebarInset = React.forwardRef<
className={cn( className={cn(
'relative flex min-h-svh flex-1 flex-col bg-background', 'relative flex min-h-svh flex-1 flex-col bg-background',
'peer-data-[variant=inset]:min-h-[calc(100svh-theme(spacing.4))] md:peer-data-[variant=inset]:m-2 md:peer-data-[state=collapsed]:peer-data-[variant=inset]:ml-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow', 'peer-data-[variant=inset]:min-h-[calc(100svh-theme(spacing.4))] md:peer-data-[variant=inset]:m-2 md:peer-data-[state=collapsed]:peer-data-[variant=inset]:ml-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow',
className className,
)} )}
{...props} {...props}
/> />
@ -351,7 +359,7 @@ const SidebarInput = React.forwardRef<
data-sidebar="input" data-sidebar="input"
className={cn( className={cn(
'h-8 w-full bg-background shadow-none focus-visible:ring-2 focus-visible:ring-sidebar-ring', 'h-8 w-full bg-background shadow-none focus-visible:ring-2 focus-visible:ring-sidebar-ring',
className className,
)} )}
{...props} {...props}
/> />
@ -414,7 +422,7 @@ const SidebarContent = React.forwardRef<
data-sidebar="content" data-sidebar="content"
className={cn( className={cn(
'flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden', 'flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden',
className className,
)} )}
{...props} {...props}
/> />
@ -450,7 +458,7 @@ const SidebarGroupLabel = React.forwardRef<
className={cn( className={cn(
'duration-200 flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 outline-none ring-sidebar-ring transition-[margin,opa] ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0', 'duration-200 flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 outline-none ring-sidebar-ring transition-[margin,opa] ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
'group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0', 'group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0',
className className,
)} )}
{...props} {...props}
/> />
@ -473,7 +481,7 @@ const SidebarGroupAction = React.forwardRef<
// Increases the hit area of the button on mobile. // Increases the hit area of the button on mobile.
'after:absolute after:-inset-2 after:md:hidden', 'after:absolute after:-inset-2 after:md:hidden',
'group-data-[collapsible=icon]:hidden', 'group-data-[collapsible=icon]:hidden',
className className,
)} )}
{...props} {...props}
/> />
@ -539,7 +547,7 @@ const sidebarMenuButtonVariants = cva(
variant: 'default', variant: 'default',
size: 'default', size: 'default',
}, },
} },
); );
const SidebarMenuButton = React.forwardRef< const SidebarMenuButton = React.forwardRef<
@ -560,7 +568,7 @@ const SidebarMenuButton = React.forwardRef<
className, className,
...props ...props
}, },
ref ref,
) => { ) => {
const Comp = asChild ? Slot : 'button'; const Comp = asChild ? Slot : 'button';
const { isMobile, state } = useSidebar(); const { isMobile, state } = useSidebar();
@ -597,7 +605,7 @@ const SidebarMenuButton = React.forwardRef<
/> />
</Tooltip> </Tooltip>
); );
} },
); );
SidebarMenuButton.displayName = 'SidebarMenuButton'; SidebarMenuButton.displayName = 'SidebarMenuButton';
@ -624,7 +632,7 @@ const SidebarMenuAction = React.forwardRef<
'group-data-[collapsible=icon]:hidden', 'group-data-[collapsible=icon]:hidden',
showOnHover && showOnHover &&
'group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 peer-data-[active=true]/menu-button:text-sidebar-accent-foreground md:opacity-0', 'group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 peer-data-[active=true]/menu-button:text-sidebar-accent-foreground md:opacity-0',
className className,
)} )}
{...props} {...props}
/> />
@ -646,7 +654,7 @@ const SidebarMenuBadge = React.forwardRef<
'peer-data-[size=default]/menu-button:top-1.5', 'peer-data-[size=default]/menu-button:top-1.5',
'peer-data-[size=lg]/menu-button:top-2.5', 'peer-data-[size=lg]/menu-button:top-2.5',
'group-data-[collapsible=icon]:hidden', 'group-data-[collapsible=icon]:hidden',
className className,
)} )}
{...props} {...props}
/> />
@ -701,7 +709,7 @@ const SidebarMenuSub = React.forwardRef<
className={cn( className={cn(
'mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l border-sidebar-border px-2.5 py-0.5', 'mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l border-sidebar-border px-2.5 py-0.5',
'group-data-[collapsible=icon]:hidden', 'group-data-[collapsible=icon]:hidden',
className className,
)} )}
{...props} {...props}
/> />
@ -736,7 +744,7 @@ const SidebarMenuSubButton = React.forwardRef<
size === 'sm' && 'text-xs', size === 'sm' && 'text-xs',
size === 'md' && 'text-sm', size === 'md' && 'text-sm',
'group-data-[collapsible=icon]:hidden', 'group-data-[collapsible=icon]:hidden',
className className,
)} )}
{...props} {...props}
/> />

View file

@ -11,13 +11,13 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
<textarea <textarea
className={cn( className={cn(
'flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50', 'flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
className className,
)} )}
ref={ref} ref={ref}
{...props} {...props}
/> />
); );
} },
); );
Textarea.displayName = 'Textarea'; Textarea.displayName = 'Textarea';

View file

@ -20,7 +20,7 @@ const TooltipContent = React.forwardRef<
sideOffset={sideOffset} sideOffset={sideOffset}
className={cn( className={cn(
'z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2', 'z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
className className,
)} )}
{...props} {...props}
/> />

View file

@ -62,7 +62,7 @@ export const VersionFooter = ({
body: JSON.stringify({ body: JSON.stringify({
timestamp: getDocumentTimestampByIndex( timestamp: getDocumentTimestampByIndex(
documents, documents,
currentVersionIndex currentVersionIndex,
), ),
}), }),
}), }),
@ -75,14 +75,14 @@ export const VersionFooter = ({
new Date( new Date(
getDocumentTimestampByIndex( getDocumentTimestampByIndex(
documents, documents,
currentVersionIndex currentVersionIndex,
) ),
) ),
) ),
), ),
] ]
: [], : [],
} },
); );
}} }}
> >

View file

@ -207,10 +207,10 @@ export function Weather({
weatherAtLocation?: WeatherAtLocation; weatherAtLocation?: WeatherAtLocation;
}) { }) {
const currentHigh = Math.max( const currentHigh = Math.max(
...weatherAtLocation.hourly.temperature_2m.slice(0, 24) ...weatherAtLocation.hourly.temperature_2m.slice(0, 24),
); );
const currentLow = Math.min( const currentLow = Math.min(
...weatherAtLocation.hourly.temperature_2m.slice(0, 24) ...weatherAtLocation.hourly.temperature_2m.slice(0, 24),
); );
const isDay = isWithinInterval(new Date(weatherAtLocation.current.time), { const isDay = isWithinInterval(new Date(weatherAtLocation.current.time), {
@ -235,17 +235,17 @@ export function Weather({
// Find the index of the current time or the next closest time // Find the index of the current time or the next closest time
const currentTimeIndex = weatherAtLocation.hourly.time.findIndex( const currentTimeIndex = weatherAtLocation.hourly.time.findIndex(
(time) => new Date(time) >= new Date(weatherAtLocation.current.time) (time) => new Date(time) >= new Date(weatherAtLocation.current.time),
); );
// Slice the arrays to get the desired number of items // Slice the arrays to get the desired number of items
const displayTimes = weatherAtLocation.hourly.time.slice( const displayTimes = weatherAtLocation.hourly.time.slice(
currentTimeIndex, currentTimeIndex,
currentTimeIndex + hoursToShow currentTimeIndex + hoursToShow,
); );
const displayTemperatures = weatherAtLocation.hourly.temperature_2m.slice( const displayTemperatures = weatherAtLocation.hourly.temperature_2m.slice(
currentTimeIndex, currentTimeIndex,
currentTimeIndex + hoursToShow currentTimeIndex + hoursToShow,
); );
return ( return (
@ -257,7 +257,7 @@ export function Weather({
}, },
{ {
'bg-indigo-900': !isDay, 'bg-indigo-900': !isDay,
} },
)} )}
> >
<div className="flex flex-row justify-between items-center"> <div className="flex flex-row justify-between items-center">
@ -270,7 +270,7 @@ export function Weather({
}, },
{ {
'bg-indigo-100': !isDay, 'bg-indigo-100': !isDay,
} },
)} )}
/> />
<div className="text-4xl font-medium text-blue-50"> <div className="text-4xl font-medium text-blue-50">
@ -296,7 +296,7 @@ export function Weather({
}, },
{ {
'bg-indigo-200': !isDay, 'bg-indigo-200': !isDay,
} },
)} )}
/> />
<div className="text-blue-50 text-sm"> <div className="text-blue-50 text-sm">

View file

@ -10,6 +10,7 @@ export default defineConfig({
out: './lib/db/migrations', out: './lib/db/migrations',
dialect: 'postgresql', dialect: 'postgresql',
dbCredentials: { dbCredentials: {
// biome-ignore lint: Forbidden non-null assertion.
url: process.env.POSTGRES_URL!, url: process.env.POSTGRES_URL!,
}, },
}); });

View file

@ -4,7 +4,7 @@ const MOBILE_BREAKPOINT = 768;
export function useIsMobile() { export function useIsMobile() {
const [isMobile, setIsMobile] = React.useState<boolean | undefined>( const [isMobile, setIsMobile] = React.useState<boolean | undefined>(
undefined undefined,
); );
React.useEffect(() => { React.useEffect(() => {

View file

@ -20,6 +20,8 @@ import {
// Optionally, if not using email/pass login, you can // Optionally, if not using email/pass login, you can
// use the Drizzle adapter for Auth.js / NextAuth // use the Drizzle adapter for Auth.js / NextAuth
// https://authjs.dev/reference/adapter/drizzle // https://authjs.dev/reference/adapter/drizzle
// biome-ignore lint: Forbidden non-null assertion.
const client = postgres(`${process.env.POSTGRES_URL!}?sslmode=require`); const client = postgres(`${process.env.POSTGRES_URL!}?sslmode=require`);
const db = drizzle(client); const db = drizzle(client);
@ -232,8 +234,8 @@ export async function deleteDocumentsByIdAfterTimestamp({
.where( .where(
and( and(
eq(suggestion.documentId, id), eq(suggestion.documentId, id),
gt(suggestion.documentCreatedAt, timestamp) gt(suggestion.documentCreatedAt, timestamp),
) ),
); );
return await db return await db
@ -241,7 +243,7 @@ export async function deleteDocumentsByIdAfterTimestamp({
.where(and(eq(document.id, id), gt(document.createdAt, timestamp))); .where(and(eq(document.id, id), gt(document.createdAt, timestamp)));
} catch (error) { } catch (error) {
console.error( console.error(
'Failed to delete documents by id after timestamp from database' 'Failed to delete documents by id after timestamp from database',
); );
throw error; throw error;
} }
@ -272,7 +274,7 @@ export async function getSuggestionsByDocumentId({
.where(and(eq(suggestion.documentId, documentId))); .where(and(eq(suggestion.documentId, documentId)));
} catch (error) { } catch (error) {
console.error( console.error(
'Failed to get suggestions by document version from database' 'Failed to get suggestions by document version from database',
); );
throw error; throw error;
} }

View file

@ -57,7 +57,7 @@ export const vote = pgTable(
return { return {
pk: primaryKey({ columns: [table.chatId, table.messageId] }), pk: primaryKey({ columns: [table.chatId, table.messageId] }),
}; };
} },
); );
export type Vote = InferSelectModel<typeof vote>; export type Vote = InferSelectModel<typeof vote>;
@ -77,7 +77,7 @@ export const document = pgTable(
return { return {
pk: primaryKey({ columns: [table.id, table.createdAt] }), pk: primaryKey({ columns: [table.id, table.createdAt] }),
}; };
} },
); );
export type Document = InferSelectModel<typeof document>; export type Document = InferSelectModel<typeof document>;
@ -103,7 +103,7 @@ export const suggestion = pgTable(
columns: [table.documentId, table.documentCreatedAt], columns: [table.documentId, table.documentCreatedAt],
foreignColumns: [document.id, document.createdAt], foreignColumns: [document.id, document.createdAt],
}), }),
}) }),
); );
export type Suggestion = InferSelectModel<typeof suggestion>; export type Suggestion = InferSelectModel<typeof suggestion>;

View file

@ -17,7 +17,7 @@ export function headingRule(level: number) {
return textblockTypeInputRule( return textblockTypeInputRule(
new RegExp(`^(#{1,${level}})\\s$`), new RegExp(`^(#{1,${level}})\\s$`),
documentSchema.nodes.heading, documentSchema.nodes.heading,
() => ({ level }) () => ({ level }),
); );
} }

View file

@ -49,7 +49,7 @@ export const patchDocumentNode = (schema, oldNode, newNode) => {
const matchedNodes = matchNodes( const matchedNodes = matchNodes(
schema, schema,
diffOldChildren, diffOldChildren,
diffNewChildren diffNewChildren,
).sort((a, b) => b.count - a.count); ).sort((a, b) => b.count - a.count);
const bestMatch = matchedNodes[0]; const bestMatch = matchedNodes[0];
if (bestMatch) { if (bestMatch) {
@ -62,11 +62,11 @@ export const patchDocumentNode = (schema, oldNode, newNode) => {
...patchRemainNodes( ...patchRemainNodes(
schema, schema,
oldBeforeMatchChildren, oldBeforeMatchChildren,
newBeforeMatchChildren newBeforeMatchChildren,
) ),
); );
finalLeftChildren.push( finalLeftChildren.push(
...diffOldChildren.slice(oldStartIndex, oldEndIndex) ...diffOldChildren.slice(oldStartIndex, oldEndIndex),
); );
const oldAfterMatchChildren = diffOldChildren.slice(oldEndIndex); const oldAfterMatchChildren = diffOldChildren.slice(oldEndIndex);
@ -76,17 +76,17 @@ export const patchDocumentNode = (schema, oldNode, newNode) => {
...patchRemainNodes( ...patchRemainNodes(
schema, schema,
oldAfterMatchChildren, oldAfterMatchChildren,
newAfterMatchChildren newAfterMatchChildren,
) ),
); );
} else { } else {
finalLeftChildren.push( finalLeftChildren.push(
...patchRemainNodes(schema, diffOldChildren, diffNewChildren) ...patchRemainNodes(schema, diffOldChildren, diffNewChildren),
); );
} }
} else { } else {
finalLeftChildren.push( finalLeftChildren.push(
...patchRemainNodes(schema, diffOldChildren, diffNewChildren) ...patchRemainNodes(schema, diffOldChildren, diffNewChildren),
); );
} }
@ -155,7 +155,7 @@ const patchRemainNodes = (schema, oldChildren, newChildren) => {
!isTextNode(rightOldNode) && matchNodeType(rightOldNode, rightNewNode); !isTextNode(rightOldNode) && matchNodeType(rightOldNode, rightNewNode);
if (Array.isArray(leftOldNode) && Array.isArray(leftNewNode)) { if (Array.isArray(leftOldNode) && Array.isArray(leftNewNode)) {
finalLeftChildren.push( finalLeftChildren.push(
...patchTextNodes(schema, leftOldNode, leftNewNode) ...patchTextNodes(schema, leftOldNode, leftNewNode),
); );
left += 1; left += 1;
continue; continue;
@ -165,7 +165,7 @@ const patchRemainNodes = (schema, oldChildren, newChildren) => {
const equalityLeft = computeChildEqualityFactor(leftOldNode, leftNewNode); const equalityLeft = computeChildEqualityFactor(leftOldNode, leftNewNode);
const equalityRight = computeChildEqualityFactor( const equalityRight = computeChildEqualityFactor(
rightOldNode, rightOldNode,
rightNewNode rightNewNode,
); );
if (equalityLeft < equalityRight) { if (equalityLeft < equalityRight) {
updateLeft = false; updateLeft = false;
@ -175,21 +175,21 @@ const patchRemainNodes = (schema, oldChildren, newChildren) => {
} }
if (updateLeft) { if (updateLeft) {
finalLeftChildren.push( finalLeftChildren.push(
patchDocumentNode(schema, leftOldNode, leftNewNode) patchDocumentNode(schema, leftOldNode, leftNewNode),
); );
left += 1; left += 1;
} else if (updateRight) { } else if (updateRight) {
finalRightChildren.unshift( finalRightChildren.unshift(
patchDocumentNode(schema, rightOldNode, rightNewNode) patchDocumentNode(schema, rightOldNode, rightNewNode),
); );
right += 1; right += 1;
} else { } else {
// Delete and insert // Delete and insert
finalLeftChildren.push( finalLeftChildren.push(
createDiffNode(schema, leftOldNode, DiffType.Deleted) createDiffNode(schema, leftOldNode, DiffType.Deleted),
); );
finalLeftChildren.push( finalLeftChildren.push(
createDiffNode(schema, leftNewNode, DiffType.Inserted) createDiffNode(schema, leftNewNode, DiffType.Inserted),
); );
left += 1; left += 1;
} }
@ -202,7 +202,7 @@ const patchRemainNodes = (schema, oldChildren, newChildren) => {
...oldChildren ...oldChildren
.slice(left, left + deleteNodeLen) .slice(left, left + deleteNodeLen)
.flat() .flat()
.map((node) => createDiffNode(schema, node, DiffType.Deleted)) .map((node) => createDiffNode(schema, node, DiffType.Deleted)),
); );
} }
@ -211,7 +211,7 @@ const patchRemainNodes = (schema, oldChildren, newChildren) => {
...newChildren ...newChildren
.slice(left, left + insertNodeLen) .slice(left, left + insertNodeLen)
.flat() .flat()
.map((node) => createDiffNode(schema, node, DiffType.Inserted)) .map((node) => createDiffNode(schema, node, DiffType.Inserted)),
); );
} }
@ -233,7 +233,7 @@ export const patchTextNodes = (schema, oldNode, newNode) => {
// Map sentences to unique characters // Map sentences to unique characters
const { chars1, chars2, lineArray } = sentencesToChars( const { chars1, chars2, lineArray } = sentencesToChars(
oldSentences, oldSentences,
newSentences newSentences,
); );
// Perform the diff // Perform the diff
@ -253,7 +253,7 @@ export const patchTextNodes = (schema, oldNode, newNode) => {
const node = createTextNode( const node = createTextNode(
schema, schema,
sentence, sentence,
type !== DiffType.Unchanged ? [createDiffMark(schema, type)] : [] type !== DiffType.Unchanged ? [createDiffMark(schema, type)] : [],
); );
return node; return node;
}); });
@ -427,7 +427,7 @@ export const createNewNode = (oldNode, children) => {
oldNode.type, oldNode.type,
oldNode.attrs, oldNode.attrs,
Fragment.fromArray(children), Fragment.fromArray(children),
oldNode.marks oldNode.marks,
); );
}; };
@ -448,8 +448,8 @@ function mapDocumentNode(node, mapper) {
Fragment.from( Fragment.from(
node.content.content node.content.content
.map((node) => mapDocumentNode(node, mapper)) .map((node) => mapDocumentNode(node, mapper))
.filter((n) => n) .filter((n) => n),
) ),
); );
return mapper(copy) || copy; return mapper(copy) || copy;
} }

View file

@ -24,7 +24,7 @@ export const buildContentFromDocument = (document: Node) => {
export const createDecorations = ( export const createDecorations = (
suggestions: Array<UISuggestion>, suggestions: Array<UISuggestion>,
view: EditorView view: EditorView,
) => { ) => {
const decorations: Array<Decoration> = []; const decorations: Array<Decoration> = [];
@ -39,8 +39,8 @@ export const createDecorations = (
{ {
suggestionId: suggestion.id, suggestionId: suggestion.id,
type: 'highlight', type: 'highlight',
} },
) ),
); );
decorations.push( decorations.push(
@ -53,8 +53,8 @@ export const createDecorations = (
{ {
suggestionId: suggestion.id, suggestionId: suggestion.id,
type: 'widget', type: 'widget',
} },
) ),
); );
} }

View file

@ -45,7 +45,7 @@ function findPositionsInDoc(doc: Node, searchText: string): Position | null {
export function projectWithPositions( export function projectWithPositions(
doc: Node, doc: Node,
suggestions: Array<Suggestion> suggestions: Array<Suggestion>,
): Array<UISuggestion> { ): Array<UISuggestion> {
return suggestions.map((suggestion) => { return suggestions.map((suggestion) => {
const positions = findPositionsInDoc(doc, suggestion.originalText); const positions = findPositionsInDoc(doc, suggestion.originalText);
@ -68,7 +68,7 @@ export function projectWithPositions(
export function createSuggestionWidget( export function createSuggestionWidget(
suggestion: UISuggestion, suggestion: UISuggestion,
view: EditorView view: EditorView,
): { dom: HTMLElement; destroy: () => void } { ): { dom: HTMLElement; destroy: () => void } {
const dom = document.createElement('span'); const dom = document.createElement('span');
const root = createRoot(dom); const root = createRoot(dom);
@ -90,7 +90,7 @@ export function createSuggestionWidget(
state.doc, state.doc,
currentDecorations.find().filter((decoration: Decoration) => { currentDecorations.find().filter((decoration: Decoration) => {
return decoration.spec.suggestionId !== suggestion.id; return decoration.spec.suggestionId !== suggestion.id;
}) }),
); );
decorationTransaction.setMeta(suggestionsPluginKey, { decorationTransaction.setMeta(suggestionsPluginKey, {
@ -103,7 +103,7 @@ export function createSuggestionWidget(
const textTransaction = view.state.tr.replaceWith( const textTransaction = view.state.tr.replaceWith(
suggestion.selectionStart, suggestion.selectionStart,
suggestion.selectionEnd, suggestion.selectionEnd,
state.schema.text(suggestion.suggestedText) state.schema.text(suggestion.suggestedText),
); );
textTransaction.setMeta('no-debounce', true); textTransaction.setMeta('no-debounce', true);

View file

@ -24,7 +24,7 @@ export const fetcher = async (url: string) => {
if (!res.ok) { if (!res.ok) {
const error = new Error( const error = new Error(
'An error occurred while fetching the data.' 'An error occurred while fetching the data.',
) as ApplicationError; ) as ApplicationError;
error.info = await res.json(); error.info = await res.json();
@ -64,7 +64,7 @@ function addToolMessageToChat({
...message, ...message,
toolInvocations: message.toolInvocations.map((toolInvocation) => { toolInvocations: message.toolInvocations.map((toolInvocation) => {
const toolResult = toolMessage.content.find( const toolResult = toolMessage.content.find(
(tool) => tool.toolCallId === toolInvocation.toolCallId (tool) => tool.toolCallId === toolInvocation.toolCallId,
); );
if (toolResult) { if (toolResult) {
@ -85,7 +85,7 @@ function addToolMessageToChat({
} }
export function convertToUIMessages( export function convertToUIMessages(
messages: Array<DBMessage> messages: Array<DBMessage>,
): Array<Message> { ): Array<Message> {
return messages.reduce((chatMessages: Array<Message>, message) => { return messages.reduce((chatMessages: Array<Message>, message) => {
if (message.role === 'tool') { if (message.role === 'tool') {
@ -127,7 +127,7 @@ export function convertToUIMessages(
} }
export function sanitizeResponseMessages( export function sanitizeResponseMessages(
messages: Array<CoreToolMessage | CoreAssistantMessage> messages: Array<CoreToolMessage | CoreAssistantMessage>,
): Array<CoreToolMessage | CoreAssistantMessage> { ): Array<CoreToolMessage | CoreAssistantMessage> {
const toolResultIds: Array<string> = []; const toolResultIds: Array<string> = [];
@ -151,7 +151,7 @@ export function sanitizeResponseMessages(
? toolResultIds.includes(content.toolCallId) ? toolResultIds.includes(content.toolCallId)
: content.type === 'text' : content.type === 'text'
? content.text.length > 0 ? content.text.length > 0
: true : true,
); );
return { return {
@ -161,7 +161,7 @@ export function sanitizeResponseMessages(
}); });
return messagesBySanitizedContent.filter( return messagesBySanitizedContent.filter(
(message) => message.content.length > 0 (message) => message.content.length > 0,
); );
} }
@ -182,7 +182,7 @@ export function sanitizeUIMessages(messages: Array<Message>): Array<Message> {
const sanitizedToolInvocations = message.toolInvocations.filter( const sanitizedToolInvocations = message.toolInvocations.filter(
(toolInvocation) => (toolInvocation) =>
toolInvocation.state === 'result' || toolInvocation.state === 'result' ||
toolResultIds.includes(toolInvocation.toolCallId) toolResultIds.includes(toolInvocation.toolCallId),
); );
return { return {
@ -194,7 +194,7 @@ export function sanitizeUIMessages(messages: Array<Message>): Array<Message> {
return messagesBySanitizedToolInvocations.filter( return messagesBySanitizedToolInvocations.filter(
(message) => (message) =>
message.content.length > 0 || message.content.length > 0 ||
(message.toolInvocations && message.toolInvocations.length > 0) (message.toolInvocations && message.toolInvocations.length > 0),
); );
} }
@ -205,7 +205,7 @@ export function getMostRecentUserMessage(messages: Array<CoreMessage>) {
export function getDocumentTimestampByIndex( export function getDocumentTimestampByIndex(
documents: Array<Document>, documents: Array<Document>,
index: number index: number,
) { ) {
if (!documents) return new Date(); if (!documents) return new Date();
if (index > documents.length) return new Date(); if (index > documents.length) return new Date();

View file

@ -6,7 +6,7 @@
"dev": "next dev --turbo", "dev": "next dev --turbo",
"build": "next build", "build": "next build",
"start": "next start", "start": "next start",
"lint": "next lint", "lint": "next lint && biome lint --write --unsafe",
"lint:fix": "next lint --fix && biome lint --write --unsafe", "lint:fix": "next lint --fix && biome lint --write --unsafe",
"format": "biome format --write", "format": "biome format --write",
"db:generate": "drizzle-kit generate", "db:generate": "drizzle-kit generate",