mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-05-06 05:42:37 +02:00
upgrade to next.js 15
This commit is contained in:
parent
8096eaf63b
commit
3b72de5df4
61 changed files with 5344 additions and 11135 deletions
|
|
@ -1,5 +1,5 @@
|
|||
"use server";
|
||||
import { getSession } from "@auth0/nextjs-auth0";
|
||||
import { auth0 } from "../lib/auth0";
|
||||
import { USE_AUTH } from "../lib/feature_flags";
|
||||
import { WithStringId, User } from "../lib/types/types";
|
||||
import { getUserFromSessionId, GUEST_DB_USER } from "../lib/auth";
|
||||
|
|
@ -12,7 +12,7 @@ export async function authCheck(): Promise<WithStringId<z.infer<typeof User>>> {
|
|||
return GUEST_DB_USER;
|
||||
}
|
||||
|
||||
const { user } = await getSession() || {};
|
||||
const { user } = await auth0.getSession() || {};
|
||||
if (!user) {
|
||||
throw new Error('User not authenticated');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -777,7 +777,7 @@ export async function generateServerAuthUrl(
|
|||
await projectAuthCheck(projectId);
|
||||
|
||||
// Get the origin from request headers
|
||||
const headersList = headers();
|
||||
const headersList = await headers();
|
||||
const host = headersList.get('host') || '';
|
||||
const protocol = headersList.get('x-forwarded-proto') || 'http';
|
||||
const origin = `${protocol}://${host}`;
|
||||
|
|
|
|||
|
|
@ -1,10 +0,0 @@
|
|||
// pages/api/auth/[auth0].js
|
||||
import { handleAuth, handleLogin } from '@auth0/nextjs-auth0';
|
||||
|
||||
export const GET = handleAuth({
|
||||
login: handleLogin({
|
||||
authorizationParams: {
|
||||
prompt: 'login'
|
||||
}
|
||||
})
|
||||
});
|
||||
|
|
@ -3,7 +3,8 @@ import { USE_BILLING } from "@/app/lib/feature_flags";
|
|||
import { redisClient } from "@/app/lib/redis";
|
||||
import { CopilotAPIRequest } from "@/app/lib/types/copilot_types";
|
||||
|
||||
export async function GET(request: Request, { params }: { params: { streamId: string } }) {
|
||||
export async function GET(request: Request, props: { params: Promise<{ streamId: string }> }) {
|
||||
const params = await props.params;
|
||||
// get the payload from redis
|
||||
const payload = await redisClient.get(`copilot-stream-${params.streamId}`);
|
||||
if (!payload) {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ import { redisClient } from "@/app/lib/redis";
|
|||
import { AgenticAPIChatMessage, AgenticAPIChatRequest, convertFromAgenticAPIChatMessages } from "@/app/lib/types/agents_api_types";
|
||||
import { createParser, type EventSourceMessage } from 'eventsource-parser';
|
||||
|
||||
export async function GET(request: Request, { params }: { params: { streamId: string } }) {
|
||||
export async function GET(request: Request, props: { params: Promise<{ streamId: string }> }) {
|
||||
const params = await props.params;
|
||||
// get the payload from redis
|
||||
const payload = await redisClient.get(`chat-stream-${params.streamId}`);
|
||||
if (!payload) {
|
||||
|
|
|
|||
|
|
@ -8,10 +8,8 @@ import { ObjectId } from 'mongodb';
|
|||
const UPLOADS_DIR = process.env.RAG_UPLOADS_DIR || '/uploads';
|
||||
|
||||
// PUT endpoint to handle file uploads
|
||||
export async function PUT(
|
||||
request: NextRequest,
|
||||
{ params }: { params: { fileId: string } }
|
||||
) {
|
||||
export async function PUT(request: NextRequest, props: { params: Promise<{ fileId: string }> }) {
|
||||
const params = await props.params;
|
||||
const fileId = params.fileId;
|
||||
if (!fileId) {
|
||||
return NextResponse.json({ error: 'Missing file ID' }, { status: 400 });
|
||||
|
|
@ -34,10 +32,8 @@ export async function PUT(
|
|||
}
|
||||
|
||||
// GET endpoint to handle file downloads
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
{ params }: { params: { fileId: string } }
|
||||
) {
|
||||
export async function GET(request: NextRequest, props: { params: Promise<{ fileId: string }> }) {
|
||||
const params = await props.params;
|
||||
const fileId = params.fileId;
|
||||
if (!fileId) {
|
||||
return NextResponse.json({ error: 'Missing file ID' }, { status: 400 });
|
||||
|
|
|
|||
|
|
@ -3,10 +3,8 @@ import { chatsCollection } from "../../../../../../lib/mongodb";
|
|||
import { ObjectId } from "mongodb";
|
||||
import { authCheck } from "../../../utils";
|
||||
|
||||
export async function POST(
|
||||
request: NextRequest,
|
||||
{ params }: { params: { chatId: string } }
|
||||
): Promise<Response> {
|
||||
export async function POST(request: NextRequest, props: { params: Promise<{ chatId: string }> }): Promise<Response> {
|
||||
const params = await props.params;
|
||||
return await authCheck(request, async (session) => {
|
||||
const { chatId } = params;
|
||||
|
||||
|
|
|
|||
|
|
@ -6,10 +6,8 @@ import { Filter, ObjectId } from "mongodb";
|
|||
import { authCheck } from "../../../utils";
|
||||
|
||||
// list messages
|
||||
export async function GET(
|
||||
req: NextRequest,
|
||||
{ params }: { params: { chatId: string } }
|
||||
): Promise<Response> {
|
||||
export async function GET(req: NextRequest, props: { params: Promise<{ chatId: string }> }): Promise<Response> {
|
||||
const params = await props.params;
|
||||
return await authCheck(req, async (session) => {
|
||||
const { chatId } = params;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,23 +1,21 @@
|
|||
'use client';
|
||||
import { TypewriterEffect } from "./lib/components/typewriter";
|
||||
import Image from 'next/image';
|
||||
import logo from "@/public/logo.png";
|
||||
import { useUser } from "@auth0/nextjs-auth0/client";
|
||||
import { useUser } from "@auth0/nextjs-auth0";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { Spinner } from "@heroui/react";
|
||||
import { LogInIcon } from "lucide-react";
|
||||
|
||||
export function App() {
|
||||
const router = useRouter();
|
||||
const { user, error, isLoading } = useUser();
|
||||
const { user, isLoading } = useUser();
|
||||
|
||||
if (user) {
|
||||
router.push("/projects");
|
||||
}
|
||||
|
||||
// Add auto-redirect for non-authenticated users
|
||||
if (!isLoading && !user && !error) {
|
||||
router.push("/api/auth/login");
|
||||
if (!isLoading && !user) {
|
||||
router.push("/auth/login");
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -30,8 +28,7 @@ export function App() {
|
|||
alt="RowBoat Logo"
|
||||
height={40}
|
||||
/>
|
||||
{(isLoading || (!user && !error)) && <Spinner size="sm" />}
|
||||
{error && <div className="text-red-500">{error.message}</div>}
|
||||
{(isLoading || !user) && <Spinner size="sm" />}
|
||||
{user && <div className="flex items-center gap-2">
|
||||
<Spinner size="sm" />
|
||||
<div className="text-sm text-gray-400">Welcome, {user.name}</div>
|
||||
|
|
|
|||
|
|
@ -4,13 +4,14 @@ import { redirect } from "next/navigation";
|
|||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
export default async function Page({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: {
|
||||
redirect: string;
|
||||
export default async function Page(
|
||||
props: {
|
||||
searchParams: Promise<{
|
||||
redirect: string;
|
||||
}>
|
||||
}
|
||||
}) {
|
||||
) {
|
||||
const searchParams = await props.searchParams;
|
||||
const customer = await requireBillingCustomer();
|
||||
await syncWithStripe(customer._id);
|
||||
const redirectUrl = searchParams.redirect as string;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,13 @@
|
|||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&display=swap');
|
||||
@import 'tailwindcss';
|
||||
@import './styles/quill-mentions.css';
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@plugin './hero.ts';
|
||||
|
||||
@source '../node_modules/@heroui/theme/dist/**/*.{js,ts,jsx,tsx}';
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
@reference 'tailwindcss';
|
||||
|
||||
@layer utilities {
|
||||
.text-balance {
|
||||
|
|
@ -106,74 +112,16 @@ html, body {
|
|||
input, textarea, select {
|
||||
@apply rounded-lg border-[#E5E7EB] dark:border-[#2E2E30]
|
||||
bg-[#F3F4F6] dark:bg-[#2A2A2D]
|
||||
focus:ring-2 focus:ring-indigo-500 focus:ring-opacity-50
|
||||
focus:ring-2 focus:ring-indigo-500/50
|
||||
transition-all duration-200;
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
|
||||
.card-shadow {
|
||||
@apply shadow-sm dark:shadow-none dark:border-border;
|
||||
}
|
||||
|
||||
.hover-effect {
|
||||
@apply hover:bg-accent/10 dark:hover:bg-accent/20 transition-colors;
|
||||
}
|
||||
|
||||
.border-subtle {
|
||||
@apply border-border dark:border-border/50;
|
||||
}
|
||||
|
||||
/* Apply rounded corners to common interactive elements by default */
|
||||
button,
|
||||
input,
|
||||
textarea,
|
||||
select,
|
||||
[role="button"],
|
||||
.card,
|
||||
.input,
|
||||
.select,
|
||||
.textarea,
|
||||
.button {
|
||||
@apply !rounded-lg;
|
||||
}
|
||||
}
|
||||
|
||||
* {
|
||||
-webkit-transition: background-color 0.2s ease-in-out, border-color 0.2s ease-in-out, opacity 0.2s ease-in-out !important;
|
||||
transition: background-color 0.2s ease-in-out, border-color 0.2s ease-in-out, opacity 0.2s ease-in-out !important;
|
||||
}
|
||||
|
||||
* {
|
||||
@apply transition-colors duration-200;
|
||||
}
|
||||
|
||||
/* Add Inter font */
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&display=swap');
|
||||
|
||||
/* Set base font */
|
||||
html {
|
||||
font-family: 'Inter', system-ui, -apple-system, sans-serif;
|
||||
}
|
||||
|
||||
@keyframes slideUpAndFade {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
body {
|
||||
background: var(--background);
|
||||
color: var(--foreground);
|
||||
}
|
||||
|
||||
.animate-slideUpAndFade {
|
||||
animation: slideUpAndFade 0.2s ease-out forwards;
|
||||
}
|
||||
3
apps/rowboat/app/hero.ts
Normal file
3
apps/rowboat/app/hero.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
// hero.ts
|
||||
import { heroui } from "@heroui/react";
|
||||
export default heroui();
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
import "./globals.css";
|
||||
import { ThemeProvider } from "./providers/theme-provider";
|
||||
import { UserProvider } from '@auth0/nextjs-auth0/client';
|
||||
import { Inter } from "next/font/google";
|
||||
import { Providers } from "./providers";
|
||||
import { Metadata } from "next";
|
||||
import { HelpModalProvider } from "./providers/help-modal-provider";
|
||||
import { Auth0Provider } from "@auth0/nextjs-auth0";
|
||||
|
||||
const inter = Inter({ subsets: ["latin"] });
|
||||
|
||||
|
|
@ -21,7 +21,7 @@ export default function RootLayout({
|
|||
children: React.ReactNode;
|
||||
}>) {
|
||||
return <html lang="en" className="h-dvh">
|
||||
<UserProvider>
|
||||
<Auth0Provider>
|
||||
<ThemeProvider>
|
||||
<body className={`${inter.className} h-full text-base [scrollbar-width:thin] bg-background`}>
|
||||
<Providers className='h-full flex flex-col'>
|
||||
|
|
@ -31,6 +31,6 @@ export default function RootLayout({
|
|||
</Providers>
|
||||
</body>
|
||||
</ThemeProvider>
|
||||
</UserProvider>
|
||||
</Auth0Provider>
|
||||
</html>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
import { z } from "zod";
|
||||
import { Claims } from "@auth0/nextjs-auth0";
|
||||
import { ObjectId } from "mongodb";
|
||||
import { usersCollection, projectsCollection, projectMembersCollection } from "./mongodb";
|
||||
import { getSession } from "@auth0/nextjs-auth0";
|
||||
import { auth0 } from "./auth0";
|
||||
import { User, WithStringId } from "./types/types";
|
||||
import { USE_AUTH } from "./feature_flags";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
export const GUEST_SESSION: Claims = {
|
||||
export const GUEST_SESSION = {
|
||||
email: "guest@rowboatlabs.com",
|
||||
email_verified: true,
|
||||
sub: "guest_user",
|
||||
|
|
@ -39,9 +38,9 @@ export async function requireAuth(): Promise<WithStringId<z.infer<typeof User>>>
|
|||
return GUEST_DB_USER;
|
||||
}
|
||||
|
||||
const { user } = await getSession() || {};
|
||||
const { user } = await auth0.getSession() || {};
|
||||
if (!user) {
|
||||
redirect('/api/auth/login');
|
||||
redirect('/auth/login');
|
||||
}
|
||||
|
||||
// fetch db user
|
||||
|
|
|
|||
21
apps/rowboat/app/lib/auth0.ts
Normal file
21
apps/rowboat/app/lib/auth0.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
// lib/auth0.js
|
||||
|
||||
import { Auth0Client } from "@auth0/nextjs-auth0/server";
|
||||
|
||||
// Initialize the Auth0 client
|
||||
export const auth0 = new Auth0Client({
|
||||
// Options are loaded from environment variables by default
|
||||
// Ensure necessary environment variables are properly set
|
||||
domain: process.env.AUTH0_ISSUER_BASE_URL,
|
||||
clientId: process.env.AUTH0_CLIENT_ID,
|
||||
clientSecret: process.env.AUTH0_CLIENT_SECRET,
|
||||
appBaseUrl: process.env.AUTH0_BASE_URL,
|
||||
secret: process.env.AUTH0_SECRET,
|
||||
|
||||
authorizationParameters: {
|
||||
// In v4, the AUTH0_SCOPE and AUTH0_AUDIENCE environment variables for API authorized applications are no longer automatically picked up by the SDK.
|
||||
// Instead, we need to provide the values explicitly.
|
||||
scope: process.env.AUTH0_SCOPE,
|
||||
audience: process.env.AUTH0_AUDIENCE,
|
||||
}
|
||||
});
|
||||
|
|
@ -3,7 +3,6 @@ import { z } from 'zod';
|
|||
import { Customer, AuthorizeRequest, AuthorizeResponse, LogUsageRequest, UsageResponse, CustomerPortalSessionResponse, PricesResponse, UpdateSubscriptionPlanRequest, UpdateSubscriptionPlanResponse, ModelsResponse } from './types/billing_types';
|
||||
import { ObjectId } from 'mongodb';
|
||||
import { projectsCollection, usersCollection } from './mongodb';
|
||||
import { getSession } from '@auth0/nextjs-auth0';
|
||||
import { redirect } from 'next/navigation';
|
||||
import { getUserFromSessionId, requireAuth } from './auth';
|
||||
import { USE_BILLING } from './feature_flags';
|
||||
|
|
|
|||
|
|
@ -9,103 +9,104 @@ export default function MarkdownContent({
|
|||
content: string;
|
||||
atValues?: Match[];
|
||||
}) {
|
||||
return <Markdown
|
||||
className="overflow-auto break-words"
|
||||
remarkPlugins={[remarkGfm]}
|
||||
components={{
|
||||
h1({ children }) {
|
||||
return <h1 className="text-xl font-bold py-2">{children}</h1>
|
||||
},
|
||||
h2({ children }) {
|
||||
return <h2 className="text-lg font-bold py-2">{children}</h2>
|
||||
},
|
||||
h3({ children }) {
|
||||
return <h3 className="text-base font-semibold py-2">{children}</h3>
|
||||
},
|
||||
h4({ children }) {
|
||||
return <h4 className="text-sm font-semibold py-2">{children}</h4>
|
||||
},
|
||||
h5({ children }) {
|
||||
return <h5 className="text-xs font-semibold py-2">{children}</h5>
|
||||
},
|
||||
h6({ children }) {
|
||||
return <h6 className="text-xs font-semibold py-2">{children}</h6>
|
||||
},
|
||||
strong({ children }) {
|
||||
return <span className="font-semibold">{children}</span>
|
||||
},
|
||||
p({ children }) {
|
||||
return <p className="py-2">{children}</p>
|
||||
},
|
||||
ul({ children }) {
|
||||
return <ul className="py-2 pl-5 list-disc">{children}</ul>
|
||||
},
|
||||
ol({ children }) {
|
||||
return <ul className="py-2 pl-5 list-decimal">{children}</ul>
|
||||
},
|
||||
table({ children }) {
|
||||
return <table className="py-2 border-collapse border border-gray-400 rounded">{children}</table>
|
||||
},
|
||||
th({ children }) {
|
||||
return <th className="px-2 py-1 border-collapse border border-gray-300 rounded">{children}</th>
|
||||
},
|
||||
td({ children }) {
|
||||
return <td className="px-2 py-1 border-collapse border border-gray-300 rounded">{children}</td>
|
||||
},
|
||||
blockquote({ children }) {
|
||||
return <blockquote className='py-2 bg-gray-200 px-1'>{children}</blockquote>;
|
||||
},
|
||||
a(props) {
|
||||
const { children, href, className, node, ...rest } = props;
|
||||
return <div className="overflow-auto break-words">
|
||||
<Markdown
|
||||
remarkPlugins={[remarkGfm]}
|
||||
components={{
|
||||
h1({ children }) {
|
||||
return <h1 className="text-xl font-bold py-2">{children}</h1>
|
||||
},
|
||||
h2({ children }) {
|
||||
return <h2 className="text-lg font-bold py-2">{children}</h2>
|
||||
},
|
||||
h3({ children }) {
|
||||
return <h3 className="text-base font-semibold py-2">{children}</h3>
|
||||
},
|
||||
h4({ children }) {
|
||||
return <h4 className="text-sm font-semibold py-2">{children}</h4>
|
||||
},
|
||||
h5({ children }) {
|
||||
return <h5 className="text-xs font-semibold py-2">{children}</h5>
|
||||
},
|
||||
h6({ children }) {
|
||||
return <h6 className="text-xs font-semibold py-2">{children}</h6>
|
||||
},
|
||||
strong({ children }) {
|
||||
return <span className="font-semibold">{children}</span>
|
||||
},
|
||||
p({ children }) {
|
||||
return <p className="py-2">{children}</p>
|
||||
},
|
||||
ul({ children }) {
|
||||
return <ul className="py-2 pl-5 list-disc">{children}</ul>
|
||||
},
|
||||
ol({ children }) {
|
||||
return <ul className="py-2 pl-5 list-decimal">{children}</ul>
|
||||
},
|
||||
table({ children }) {
|
||||
return <table className="py-2 border-collapse border border-gray-400 rounded">{children}</table>
|
||||
},
|
||||
th({ children }) {
|
||||
return <th className="px-2 py-1 border-collapse border border-gray-300 rounded">{children}</th>
|
||||
},
|
||||
td({ children }) {
|
||||
return <td className="px-2 py-1 border-collapse border border-gray-300 rounded">{children}</td>
|
||||
},
|
||||
blockquote({ children }) {
|
||||
return <blockquote className='py-2 bg-gray-200 px-1'>{children}</blockquote>;
|
||||
},
|
||||
a(props) {
|
||||
const { children, href, className, node, ...rest } = props;
|
||||
|
||||
// If this is a mention link, render it with mention styling
|
||||
if (href === '#mention') {
|
||||
let label: string = '';
|
||||
// Check if children is an array and get the first text element
|
||||
if (Array.isArray(children) && children.length > 0) {
|
||||
const text = children[0];
|
||||
if (typeof text === 'string') {
|
||||
const parts = text.split('@');
|
||||
// If this is a mention link, render it with mention styling
|
||||
if (href === '#mention') {
|
||||
let label: string = '';
|
||||
// Check if children is an array and get the first text element
|
||||
if (Array.isArray(children) && children.length > 0) {
|
||||
const text = children[0];
|
||||
if (typeof text === 'string') {
|
||||
const parts = text.split('@');
|
||||
if (parts.length === 2) {
|
||||
label = parts[1];
|
||||
}
|
||||
}
|
||||
} else if (typeof children === 'string') {
|
||||
// Fallback for direct string children
|
||||
const parts = children.split('@');
|
||||
if (parts.length === 2) {
|
||||
label = parts[1];
|
||||
}
|
||||
}
|
||||
} else if (typeof children === 'string') {
|
||||
// Fallback for direct string children
|
||||
const parts = children.split('@');
|
||||
if (parts.length === 2) {
|
||||
label = parts[1];
|
||||
}
|
||||
}
|
||||
|
||||
// check if the the mention is valid
|
||||
const invalid = !atValues.some(atValue => atValue.id === label);
|
||||
if (atValues.length > 0 && invalid) {
|
||||
// check if the the mention is valid
|
||||
const invalid = !atValues.some(atValue => atValue.id === label);
|
||||
if (atValues.length > 0 && invalid) {
|
||||
return (
|
||||
<span className="inline-block bg-[#e0f2fe] text-[red] px-1.5 py-0.5 rounded whitespace-nowrap">
|
||||
@{label} (!)
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<span className="inline-block bg-[#e0f2fe] text-[red] px-1.5 py-0.5 rounded whitespace-nowrap">
|
||||
@{label} (!)
|
||||
<span className="inline-block bg-[#e0f2fe] text-[#1e40af] px-1.5 py-0.5 rounded whitespace-nowrap">
|
||||
@{label}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<span className="inline-block bg-[#e0f2fe] text-[#1e40af] px-1.5 py-0.5 rounded whitespace-nowrap">
|
||||
@{label}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
// Otherwise render normal link (your existing link component)
|
||||
return <a className="inline-flex items-center gap-1" target="_blank" href={href} {...rest} >
|
||||
<span className='underline'>
|
||||
{children}
|
||||
</span>
|
||||
<svg className="w-[16px] h-[16px]" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
|
||||
<path stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1" d="M18 14v4.833A1.166 1.166 0 0 1 16.833 20H5.167A1.167 1.167 0 0 1 4 18.833V7.167A1.166 1.166 0 0 1 5.167 6h4.618m4.447-2H20v5.768m-7.889 2.121 7.778-7.778" />
|
||||
</svg>
|
||||
</a>
|
||||
},
|
||||
}}
|
||||
>
|
||||
{content}
|
||||
</Markdown>;
|
||||
// Otherwise render normal link (your existing link component)
|
||||
return <a className="inline-flex items-center gap-1" target="_blank" href={href} {...rest} >
|
||||
<span className='underline'>
|
||||
{children}
|
||||
</span>
|
||||
<svg className="w-[16px] h-[16px]" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
|
||||
<path stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1" d="M18 14v4.833A1.166 1.166 0 0 1 16.833 20H5.167A1.167 1.167 0 0 1 4 18.833V7.167A1.166 1.166 0 0 1 5.167 6h4.618m4.447-2H20v5.768m-7.889 2.121 7.778-7.778" />
|
||||
</svg>
|
||||
</a>
|
||||
},
|
||||
}}
|
||||
>
|
||||
{content}
|
||||
</Markdown>
|
||||
</div>;
|
||||
}
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
@import "../../globals.css";
|
||||
|
||||
/* Target both edit mode and view mode mentions */
|
||||
.mention,
|
||||
.ql-editor p span[class*="bg-[#e"], /* Matches both #e8f2fe and #e0f2fe */
|
||||
|
|
@ -15,12 +17,12 @@ span[class*="bg-[#e"] { /* For view mode */
|
|||
}
|
||||
|
||||
/* Handle Next.js dark mode class if needed */
|
||||
:global(.dark) .mention,
|
||||
/* :global(.dark) .mention,
|
||||
:global(.dark) .ql-editor p span[class*="bg-[#e"],
|
||||
:global(.dark) span[class*="bg-[#e"] {
|
||||
background-color: rgb(31 41 55) !important;
|
||||
color: rgb(243 244 246) !important;
|
||||
}
|
||||
} */
|
||||
|
||||
/* Override the inline styles */
|
||||
.ql-editor p span[class*="bg-[#e0f2fe]"],
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ export function ListItem({
|
|||
onClick: () => void;
|
||||
disabled?: boolean;
|
||||
rightElement?: React.ReactNode;
|
||||
selectedRef?: React.RefObject<HTMLButtonElement>;
|
||||
selectedRef?: React.RefObject<HTMLButtonElement | null>;
|
||||
icon?: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
'use client';
|
||||
import { useUser } from '@auth0/nextjs-auth0/client';
|
||||
import { useUser } from '@auth0/nextjs-auth0';
|
||||
import { Avatar, Dropdown, DropdownItem, DropdownSection, DropdownTrigger, DropdownMenu } from "@heroui/react";
|
||||
import { useRouter } from 'next/navigation';
|
||||
import Link from 'next/link';
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
'use client';
|
||||
import { Spinner } from "@heroui/react";
|
||||
|
||||
export default function Loading() {
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ export function Section({
|
|||
title: string;
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return <div className="w-full flex flex-col gap-4 border border-border p-4 rounded-md">
|
||||
<h2 className="font-semibold pb-2 border-b border-border">{title}</h2>
|
||||
return <div className="w-full flex flex-col gap-4 border border p-4 rounded-md">
|
||||
<h2 className="font-semibold pb-2 border-b border">{title}</h2>
|
||||
{children}
|
||||
</div>;
|
||||
}
|
||||
|
|
@ -198,9 +198,9 @@ export function ApiKeysSection({
|
|||
|
||||
<Divider />
|
||||
{loading && <Spinner size="sm" />}
|
||||
{!loading && <div className="border border-border rounded-lg text-sm">
|
||||
<div className="flex items-center border-b border-border p-4">
|
||||
<div className="flex-[3] font-normal">API Key</div>
|
||||
{!loading && <div className="border border rounded-lg text-sm">
|
||||
<div className="flex items-center border-b border p-4">
|
||||
<div className="flex-3 font-normal">API Key</div>
|
||||
<div className="flex-1 font-normal">Created</div>
|
||||
<div className="flex-1 font-normal">Last Used</div>
|
||||
<div className="w-10"></div>
|
||||
|
|
@ -216,8 +216,8 @@ export function ApiKeysSection({
|
|||
</div>}
|
||||
<div className="flex flex-col">
|
||||
{keys.map((key) => (
|
||||
<div key={key._id} className="flex items-start border-b border-border last:border-b-0 p-4">
|
||||
<div className="flex-[3] p-2">
|
||||
<div key={key._id} className="flex items-start border-b border last:border-b-0 p-4">
|
||||
<div className="flex-3 p-2">
|
||||
<ApiKeyDisplay apiKey={key.key} />
|
||||
</div>
|
||||
<div className="flex-1 p-2">
|
||||
|
|
|
|||
|
|
@ -7,13 +7,14 @@ export const metadata: Metadata = {
|
|||
title: "Project config",
|
||||
};
|
||||
|
||||
export default async function Page({
|
||||
params,
|
||||
}: {
|
||||
params: {
|
||||
projectId: string;
|
||||
};
|
||||
}) {
|
||||
export default async function Page(
|
||||
props: {
|
||||
params: Promise<{
|
||||
projectId: string;
|
||||
}>;
|
||||
}
|
||||
) {
|
||||
const params = await props.params;
|
||||
await requireActiveBillingSubscription();
|
||||
return <App
|
||||
projectId={params.projectId}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ export default async function Layout({
|
|||
params,
|
||||
children
|
||||
}: {
|
||||
params: { projectId: string }
|
||||
params: Promise<{ projectId: string }>
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return children;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import { redirect } from "next/navigation";
|
||||
import { requireActiveBillingSubscription } from '@/app/lib/billing';
|
||||
|
||||
export default async function Page({
|
||||
params
|
||||
}: {
|
||||
params: { projectId: string }
|
||||
}) {
|
||||
export default async function Page(
|
||||
props: {
|
||||
params: Promise<{ projectId: string }>
|
||||
}
|
||||
) {
|
||||
const params = await props.params;
|
||||
await requireActiveBillingSubscription();
|
||||
redirect(`/projects/${params.projectId}/workflow`);
|
||||
}
|
||||
|
|
@ -1,14 +1,15 @@
|
|||
import { SourcePage } from "./source-page";
|
||||
import { requireActiveBillingSubscription } from '@/app/lib/billing';
|
||||
|
||||
export default async function Page({
|
||||
params,
|
||||
}: {
|
||||
params: {
|
||||
projectId: string,
|
||||
sourceId: string
|
||||
export default async function Page(
|
||||
props: {
|
||||
params: Promise<{
|
||||
projectId: string,
|
||||
sourceId: string
|
||||
}>
|
||||
}
|
||||
}) {
|
||||
) {
|
||||
const params = await props.params;
|
||||
await requireActiveBillingSubscription();
|
||||
return <SourcePage projectId={params.projectId} sourceId={params.sourceId} />;
|
||||
}
|
||||
|
|
@ -37,7 +37,7 @@ export function SectionRow({ children, className }: { children: ReactNode; class
|
|||
|
||||
export function SectionLabel({ children, className }: { children: ReactNode; className?: string }) {
|
||||
return (
|
||||
<div className={`w-24 flex-shrink-0 text-sm text-gray-500 dark:text-gray-400 ${className || ''}`}>
|
||||
<div className={`w-24 shrink-0 text-sm text-gray-500 dark:text-gray-400 ${className || ''}`}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ export function SourceStatus({
|
|||
|
||||
{status === 'pending' && (
|
||||
<>
|
||||
<div className="flex-shrink-0">
|
||||
<div className="shrink-0">
|
||||
<Spinner size="sm" className="text-blue-500 dark:text-blue-400" />
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ export function ToggleSource({
|
|||
onClick={handleToggle}
|
||||
disabled={loading}
|
||||
className={`
|
||||
relative inline-flex h-5 w-9 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent
|
||||
relative inline-flex h-5 w-9 shrink-0 cursor-pointer rounded-full border-2 border-transparent
|
||||
transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-500/20
|
||||
${isActive ? 'bg-indigo-500' : 'bg-gray-200 dark:bg-gray-700'}
|
||||
disabled:opacity-50 disabled:cursor-not-allowed
|
||||
|
|
|
|||
|
|
@ -8,11 +8,12 @@ export const metadata: Metadata = {
|
|||
title: "Add data source"
|
||||
}
|
||||
|
||||
export default async function Page({
|
||||
params
|
||||
}: {
|
||||
params: { projectId: string }
|
||||
}) {
|
||||
export default async function Page(
|
||||
props: {
|
||||
params: Promise<{ projectId: string }>
|
||||
}
|
||||
) {
|
||||
const params = await props.params;
|
||||
await requireActiveBillingSubscription();
|
||||
if (!USE_RAG) {
|
||||
redirect(`/projects/${params.projectId}`);
|
||||
|
|
|
|||
|
|
@ -6,11 +6,12 @@ export const metadata: Metadata = {
|
|||
title: "Data sources",
|
||||
}
|
||||
|
||||
export default async function Page({
|
||||
params,
|
||||
}: {
|
||||
params: { projectId: string }
|
||||
}) {
|
||||
export default async function Page(
|
||||
props: {
|
||||
params: Promise<{ projectId: string }>
|
||||
}
|
||||
) {
|
||||
const params = await props.params;
|
||||
await requireActiveBillingSubscription();
|
||||
return <SourcesList
|
||||
projectId={params.projectId}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ interface ProfileFormProps {
|
|||
mockTools?: boolean;
|
||||
mockPrompt?: string;
|
||||
};
|
||||
formRef: React.RefObject<HTMLFormElement>;
|
||||
formRef: React.RefObject<HTMLFormElement | null>;
|
||||
handleSubmit: (formData: FormData) => Promise<void>;
|
||||
onCancel: () => void;
|
||||
submitButtonText: string;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { FormStatusButton } from "@/app/lib/components/form-status-button";
|
|||
import { Button, Input, Textarea } from "@heroui/react";
|
||||
|
||||
interface ScenarioFormProps {
|
||||
formRef: React.RefObject<HTMLFormElement>;
|
||||
formRef: React.RefObject<HTMLFormElement | null>;
|
||||
handleSubmit: (formData: FormData) => Promise<void>;
|
||||
onCancel: () => void;
|
||||
submitButtonText: string;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { ProfileSelector } from "@/app/projects/[projectId]/test/[[...slug]]/com
|
|||
import { z } from "zod";
|
||||
|
||||
interface SimulationFormProps {
|
||||
formRef: React.RefObject<HTMLFormElement>;
|
||||
formRef: React.RefObject<HTMLFormElement | null>;
|
||||
handleSubmit: (formData: FormData) => Promise<void>;
|
||||
scenario: WithStringId<z.infer<typeof TestScenario>> | null;
|
||||
setScenario: (scenario: WithStringId<z.infer<typeof TestScenario>> | null) => void;
|
||||
|
|
|
|||
|
|
@ -6,11 +6,12 @@ import { RunsApp } from "./runs_app";
|
|||
import { TestingMenu } from "./testing_menu";
|
||||
import { requireActiveBillingSubscription } from '@/app/lib/billing';
|
||||
|
||||
export default async function TestPage({ params }: { params: { projectId: string; slug?: string[] } }) {
|
||||
export default async function TestPage(props: { params: Promise<{ projectId: string; slug?: string[] }> }) {
|
||||
const params = await props.params;
|
||||
await requireActiveBillingSubscription();
|
||||
const { projectId, slug = [] } = params;
|
||||
let app: "scenarios" | "simulations" | "profiles" | "runs" = "runs";
|
||||
|
||||
|
||||
if (slug[0] === "scenarios") {
|
||||
app = "scenarios";
|
||||
} else if (slug[0] === "simulations") {
|
||||
|
|
|
|||
|
|
@ -295,7 +295,7 @@ export function CustomServers() {
|
|||
<div className="space-y-6">
|
||||
<div className="bg-blue-50 dark:bg-blue-900/20 border border-blue-100 dark:border-blue-800 rounded-lg p-4">
|
||||
<div className="flex gap-3">
|
||||
<div className="flex-shrink-0">
|
||||
<div className="shrink-0">
|
||||
<Info className="h-5 w-5 text-blue-600 dark:text-blue-400" />
|
||||
</div>
|
||||
<p className="text-sm text-blue-700 dark:text-blue-300">
|
||||
|
|
|
|||
|
|
@ -578,7 +578,7 @@ export function HostedServers({ onSwitchTab }: HostedServersProps) {
|
|||
<div className="space-y-6">
|
||||
<div className="bg-blue-50 dark:bg-blue-900/20 border border-blue-100 dark:border-blue-800 rounded-lg p-4">
|
||||
<div className="flex gap-3">
|
||||
<div className="flex-shrink-0">
|
||||
<div className="shrink-0">
|
||||
<Info className="h-5 w-5 text-blue-600 dark:text-blue-400" />
|
||||
</div>
|
||||
<p className="text-sm text-blue-700 dark:text-blue-300">
|
||||
|
|
|
|||
|
|
@ -193,7 +193,7 @@ export function TestToolModal({ isOpen, onClose, tool, server }: TestToolModalPr
|
|||
value={item || ''}
|
||||
onChange={(e) => handleArrayItemChange(index, e.target.value)}
|
||||
placeholder="Enter value"
|
||||
className="focus:ring-0 focus:ring-offset-0 !ring-0 !ring-offset-0 focus:outline-none"
|
||||
className="focus:ring-0 focus:ring-offset-0 ring-0! ring-offset-0! focus:outline-none"
|
||||
/>
|
||||
) : itemSchema.type === 'number' || itemSchema.type === 'integer' ? (
|
||||
<Input
|
||||
|
|
@ -209,7 +209,7 @@ export function TestToolModal({ isOpen, onClose, tool, server }: TestToolModalPr
|
|||
handleArrayItemChange(index, isNaN(val) ? '' : val);
|
||||
}}
|
||||
placeholder="Enter value"
|
||||
className="focus:ring-0 focus:ring-offset-0 !ring-0 !ring-offset-0 focus:outline-none"
|
||||
className="focus:ring-0 focus:ring-offset-0 ring-0! ring-offset-0! focus:outline-none"
|
||||
/>
|
||||
) : itemSchema.type === 'boolean' ? (
|
||||
<div className="scale-75 origin-left">
|
||||
|
|
@ -284,7 +284,7 @@ export function TestToolModal({ isOpen, onClose, tool, server }: TestToolModalPr
|
|||
className="w-full px-3 py-2 text-sm border border-gray-200 dark:border-gray-700 rounded-md
|
||||
bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100
|
||||
focus:outline-none hover:border-gray-300 dark:hover:border-gray-600
|
||||
focus:ring-0 focus:ring-offset-0 !ring-0 !ring-offset-0"
|
||||
focus:ring-0 focus:ring-offset-0 ring-0! ring-offset-0!"
|
||||
>
|
||||
<option value="" disabled>Select {paramName}</option>
|
||||
{schema.enum.map((opt: string) => (
|
||||
|
|
@ -299,7 +299,7 @@ export function TestToolModal({ isOpen, onClose, tool, server }: TestToolModalPr
|
|||
type="datetime-local"
|
||||
value={value}
|
||||
onChange={(e) => handleParameterChange(paramName, e.target.value)}
|
||||
className="focus:ring-0 focus:ring-offset-0 !ring-0 !ring-offset-0 focus:outline-none"
|
||||
className="focus:ring-0 focus:ring-offset-0 ring-0! ring-offset-0! focus:outline-none"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -309,7 +309,7 @@ export function TestToolModal({ isOpen, onClose, tool, server }: TestToolModalPr
|
|||
type="date"
|
||||
value={value}
|
||||
onChange={(e) => handleParameterChange(paramName, e.target.value)}
|
||||
className="focus:ring-0 focus:ring-offset-0 !ring-0 !ring-offset-0 focus:outline-none"
|
||||
className="focus:ring-0 focus:ring-offset-0 ring-0! ring-offset-0! focus:outline-none"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -319,7 +319,7 @@ export function TestToolModal({ isOpen, onClose, tool, server }: TestToolModalPr
|
|||
type="time"
|
||||
value={value}
|
||||
onChange={(e) => handleParameterChange(paramName, e.target.value)}
|
||||
className="focus:ring-0 focus:ring-offset-0 !ring-0 !ring-offset-0 focus:outline-none"
|
||||
className="focus:ring-0 focus:ring-offset-0 ring-0! ring-offset-0! focus:outline-none"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -329,7 +329,7 @@ export function TestToolModal({ isOpen, onClose, tool, server }: TestToolModalPr
|
|||
value={value}
|
||||
onChange={(e) => handleParameterChange(paramName, e.target.value)}
|
||||
placeholder={`Enter ${paramName}`}
|
||||
className="focus:ring-0 focus:ring-offset-0 !ring-0 !ring-offset-0 focus:outline-none"
|
||||
className="focus:ring-0 focus:ring-offset-0 ring-0! ring-offset-0! focus:outline-none"
|
||||
/>
|
||||
);
|
||||
|
||||
|
|
@ -349,7 +349,7 @@ export function TestToolModal({ isOpen, onClose, tool, server }: TestToolModalPr
|
|||
handleParameterChange(paramName, isNaN(val) ? '' : val);
|
||||
}}
|
||||
placeholder={`Enter ${paramName}`}
|
||||
className="focus:ring-0 focus:ring-offset-0 !ring-0 !ring-offset-0 focus:outline-none"
|
||||
className="focus:ring-0 focus:ring-offset-0 ring-0! ring-offset-0! focus:outline-none"
|
||||
/>
|
||||
);
|
||||
|
||||
|
|
@ -377,7 +377,7 @@ export function TestToolModal({ isOpen, onClose, tool, server }: TestToolModalPr
|
|||
value={value}
|
||||
onChange={(e) => handleParameterChange(paramName, e.target.value)}
|
||||
placeholder={`Enter ${paramName}`}
|
||||
className="focus:ring-0 focus:ring-offset-0 !ring-0 !ring-offset-0 focus:outline-none"
|
||||
className="focus:ring-0 focus:ring-offset-0 ring-0! ring-offset-0! focus:outline-none"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ export function ToolsConfig() {
|
|||
<Tab key="hosted" title={
|
||||
<div className="flex items-center gap-2">
|
||||
<span>Tools Library</span>
|
||||
<span className="leading-none px-1.5 py-[2px] text-[9px] font-medium bg-gradient-to-r from-pink-500 to-violet-500 text-white rounded-full">
|
||||
<span className="leading-none px-1.5 py-[2px] text-[9px] font-medium bg-linear-to-r from-pink-500 to-violet-500 text-white rounded-full">
|
||||
BETA
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ function Section({ title, children, description }: {
|
|||
|
||||
export function WebhookConfig() {
|
||||
const params = useParams();
|
||||
const projectId = typeof params.projectId === 'string' ? params.projectId : params.projectId[0];
|
||||
const projectId = params.projectId ? (typeof params.projectId === 'string' ? params.projectId : params.projectId[0]) : '';
|
||||
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [webhookUrl, setWebhookUrl] = useState<string | null>(null);
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ const ListItemWithMenu = ({
|
|||
isSelected?: boolean;
|
||||
onClick?: () => void;
|
||||
disabled?: boolean;
|
||||
selectedRef?: React.RefObject<HTMLButtonElement>;
|
||||
selectedRef?: React.RefObject<HTMLButtonElement | null>;
|
||||
menuContent: React.ReactNode;
|
||||
statusLabel?: React.ReactNode;
|
||||
icon?: React.ReactNode;
|
||||
|
|
@ -111,7 +111,7 @@ const ListItemWithMenu = ({
|
|||
}}
|
||||
disabled={disabled}
|
||||
>
|
||||
<div className={clsx("flex-shrink-0 flex items-center justify-center w-4 h-4", iconClassName)}>
|
||||
<div className={clsx("shrink-0 flex items-center justify-center w-4 h-4", iconClassName)}>
|
||||
{mcpServerName ? (
|
||||
<ServerLogo
|
||||
serverName={mcpServerName}
|
||||
|
|
@ -147,7 +147,7 @@ interface ServerCardProps {
|
|||
} | null;
|
||||
onSelectTool: (name: string) => void;
|
||||
onDeleteTool: (name: string) => void;
|
||||
selectedRef: React.RefObject<HTMLButtonElement>;
|
||||
selectedRef: React.RefObject<HTMLButtonElement | null>;
|
||||
}
|
||||
|
||||
const ServerCard = ({
|
||||
|
|
@ -343,7 +343,7 @@ export function EntityList({
|
|||
tourTarget="entity-agents"
|
||||
className={clsx(
|
||||
"h-full overflow-hidden",
|
||||
!expandedPanels.agents && "!h-[53px]"
|
||||
!expandedPanels.agents && "h-[53px]!"
|
||||
)}
|
||||
title={
|
||||
<button
|
||||
|
|
@ -429,7 +429,7 @@ export function EntityList({
|
|||
tourTarget="entity-tools"
|
||||
className={clsx(
|
||||
"h-full overflow-hidden",
|
||||
!expandedPanels.tools && "!h-[53px]"
|
||||
!expandedPanels.tools && "h-[53px]!"
|
||||
)}
|
||||
title={
|
||||
<button
|
||||
|
|
@ -549,7 +549,7 @@ export function EntityList({
|
|||
tourTarget="entity-prompts"
|
||||
className={clsx(
|
||||
"h-full overflow-hidden",
|
||||
!expandedPanels.prompts && "!h-[53px]"
|
||||
!expandedPanels.prompts && "h-[53px]!"
|
||||
)}
|
||||
title={
|
||||
<button
|
||||
|
|
@ -696,7 +696,7 @@ const SortableAgentItem = ({ agent, isSelected, onClick, selectedRef, statusLabe
|
|||
agent: z.infer<typeof WorkflowAgent>;
|
||||
isSelected?: boolean;
|
||||
onClick?: () => void;
|
||||
selectedRef?: React.RefObject<HTMLButtonElement>;
|
||||
selectedRef?: React.RefObject<HTMLButtonElement | null>;
|
||||
statusLabel?: React.ReactNode;
|
||||
onToggle: (name: string) => void;
|
||||
onSetMainAgent: (name: string) => void;
|
||||
|
|
|
|||
|
|
@ -11,11 +11,12 @@ export const metadata: Metadata = {
|
|||
title: "Workflow"
|
||||
}
|
||||
|
||||
export default async function Page({
|
||||
params,
|
||||
}: {
|
||||
params: { projectId: string };
|
||||
}) {
|
||||
export default async function Page(
|
||||
props: {
|
||||
params: Promise<{ projectId: string }>;
|
||||
}
|
||||
) {
|
||||
const params = await props.params;
|
||||
await requireActiveBillingSubscription();
|
||||
console.log('->>> workflow page being rendered');
|
||||
const project = await projectsCollection.findOne({
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ function PreviewModal({
|
|||
</div>}
|
||||
{view === 'markdown' && <div className="flex gap-1">
|
||||
{oldValue !== undefined && <div className="w-1/2 flex flex-col border-r-2 border-gray-200 overflow-auto">
|
||||
<div className="text-gray-800 font-semibold italic text-sm px-2 py-1 border-b-1 border-gray-200">Old</div>
|
||||
<div className="text-gray-800 font-semibold italic text-sm px-2 py-1 border-b border-gray-200">Old</div>
|
||||
<div className="p-2 overflow-auto">
|
||||
<MarkdownContent
|
||||
content={oldValue}
|
||||
|
|
@ -158,7 +158,7 @@ function PreviewModal({
|
|||
<div className={clsx("flex flex-col", {
|
||||
'w-1/2': oldValue !== undefined
|
||||
})}>
|
||||
{oldValue !== undefined && <div className="text-gray-800 font-semibold italic text-sm px-2 py-1 border-b-1 border-gray-200">New</div>}
|
||||
{oldValue !== undefined && <div className="text-gray-800 font-semibold italic text-sm px-2 py-1 border-b border-gray-200">New</div>}
|
||||
<div className="p-2 overflow-auto">
|
||||
<MarkdownContent
|
||||
content={newValue}
|
||||
|
|
|
|||
|
|
@ -580,7 +580,7 @@ export function WorkflowEditor({
|
|||
eligibleModels: z.infer<typeof ModelsResponse> | "*";
|
||||
}) {
|
||||
|
||||
const [state, dispatch] = useReducer<Reducer<State, Action>>(reducer, {
|
||||
const [state, dispatch] = useReducer(reducer, {
|
||||
patches: [],
|
||||
inversePatches: [],
|
||||
currentIndex: 0,
|
||||
|
|
|
|||
|
|
@ -1,26 +1,20 @@
|
|||
import Link from "next/link";
|
||||
import { LucideIcon } from "lucide-react";
|
||||
|
||||
interface MenuItemProps {
|
||||
href?: string;
|
||||
icon: LucideIcon;
|
||||
selected?: boolean;
|
||||
collapsed?: boolean;
|
||||
onClick?: () => void;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
export default function MenuItem({
|
||||
href,
|
||||
icon: Icon,
|
||||
selected = false,
|
||||
collapsed = false,
|
||||
onClick,
|
||||
children
|
||||
}: MenuItemProps) {
|
||||
const ButtonContent = (
|
||||
<button
|
||||
onClick={onClick}
|
||||
return (
|
||||
<div
|
||||
className={`
|
||||
w-full px-3 py-2 rounded-md flex items-center gap-3
|
||||
text-sm font-medium transition-all duration-200
|
||||
|
|
@ -32,12 +26,6 @@ export default function MenuItem({
|
|||
>
|
||||
<Icon size={16} />
|
||||
{!collapsed && children}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
|
||||
if (href) {
|
||||
return <Link href={href}>{ButtonContent}</Link>;
|
||||
}
|
||||
|
||||
return ButtonContent;
|
||||
}
|
||||
|
|
@ -98,7 +98,7 @@ export default function Sidebar({ projectId, useRag, useAuth, collapsed = false,
|
|||
return (
|
||||
<>
|
||||
<aside className={`${collapsed ? 'w-16' : 'w-60'} bg-transparent flex flex-col h-full transition-all duration-300`}>
|
||||
<div className="flex flex-col flex-grow">
|
||||
<div className="flex flex-col grow">
|
||||
{!isProjectsRoute && (
|
||||
<>
|
||||
{/* Project Selector */}
|
||||
|
|
@ -141,46 +141,42 @@ export default function Sidebar({ projectId, useRag, useAuth, collapsed = false,
|
|||
>
|
||||
<Link
|
||||
href={isDisabled ? '#' : fullPath}
|
||||
className={isDisabled ? 'pointer-events-none' : ''}
|
||||
className={`
|
||||
relative w-full rounded-md flex items-center
|
||||
text-[15px] font-medium transition-all duration-200
|
||||
${collapsed ? 'justify-center py-4' : 'px-2.5 py-3 gap-2.5'}
|
||||
${isActive
|
||||
? 'bg-indigo-50 dark:bg-indigo-500/10 text-indigo-600 dark:text-indigo-400 border-l-2 border-indigo-600 dark:border-indigo-400'
|
||||
: isDisabled
|
||||
? 'text-zinc-300 dark:text-zinc-600 cursor-not-allowed'
|
||||
: 'text-zinc-600 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800/50 hover:text-zinc-900 dark:hover:text-zinc-300'
|
||||
}
|
||||
${isDisabled ? 'pointer-events-none' : ''}
|
||||
`}
|
||||
data-tour-target={item.href === 'config' ? 'settings' : item.href === 'sources' ? 'entity-data-sources' : undefined}
|
||||
>
|
||||
<button
|
||||
<Icon
|
||||
size={collapsed ? COLLAPSED_ICON_SIZE : EXPANDED_ICON_SIZE}
|
||||
className={`
|
||||
relative w-full rounded-md flex items-center
|
||||
text-[15px] font-medium transition-all duration-200
|
||||
${collapsed ? 'justify-center py-4' : 'px-2.5 py-3 gap-2.5'}
|
||||
${isActive
|
||||
? 'bg-indigo-50 dark:bg-indigo-500/10 text-indigo-600 dark:text-indigo-400 border-l-2 border-indigo-600 dark:border-indigo-400'
|
||||
: isDisabled
|
||||
? 'text-zinc-300 dark:text-zinc-600 cursor-not-allowed'
|
||||
: 'text-zinc-600 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800/50 hover:text-zinc-900 dark:hover:text-zinc-300'
|
||||
transition-all duration-200
|
||||
${isDisabled
|
||||
? 'text-zinc-300 dark:text-zinc-600'
|
||||
: isActive
|
||||
? 'text-indigo-600 dark:text-indigo-400'
|
||||
: 'text-zinc-500 dark:text-zinc-400'
|
||||
}
|
||||
`}
|
||||
disabled={isDisabled}
|
||||
data-tour-target={item.href === 'config' ? 'settings' : item.href === 'sources' ? 'entity-data-sources' : undefined}
|
||||
>
|
||||
<Icon
|
||||
size={collapsed ? COLLAPSED_ICON_SIZE : EXPANDED_ICON_SIZE}
|
||||
className={`
|
||||
transition-all duration-200
|
||||
${isDisabled
|
||||
? 'text-zinc-300 dark:text-zinc-600'
|
||||
: isActive
|
||||
? 'text-indigo-600 dark:text-indigo-400'
|
||||
: 'text-zinc-500 dark:text-zinc-400'
|
||||
}
|
||||
`}
|
||||
/>
|
||||
{!collapsed && (
|
||||
<>
|
||||
<span>{item.label}</span>
|
||||
{item.beta && (
|
||||
<span className="ml-1.5 leading-none px-1.5 py-[2px] text-[9px] font-medium bg-gradient-to-r from-pink-500 to-violet-500 text-white rounded-full">
|
||||
BETA
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
/>
|
||||
{!collapsed && (
|
||||
<>
|
||||
<span>{item.label}</span>
|
||||
{item.beta && (
|
||||
<span className="ml-1.5 leading-none px-1.5 py-[2px] text-[9px] font-medium bg-linear-to-r from-pink-500 to-violet-500 text-white rounded-full">
|
||||
BETA
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Link>
|
||||
</Tooltip>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ export function Nav({
|
|||
setCollapsed(!collapsed);
|
||||
}
|
||||
|
||||
return <div className={clsx("shrink-0 flex flex-col gap-2 border-r border-border relative p-2", {
|
||||
return <div className={clsx("shrink-0 flex flex-col gap-2 border-r border relative p-2", {
|
||||
"w-40": !collapsed,
|
||||
"w-10": collapsed
|
||||
})}>
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ export function SearchInput({
|
|||
tokens.colors.dark.text.primary,
|
||||
"placeholder:text-gray-400 dark:placeholder:text-gray-500",
|
||||
"border border-gray-200 dark:border-gray-700",
|
||||
"focus:ring-2 focus:ring-indigo-500 focus:ring-opacity-50",
|
||||
"focus:ring-2 focus:ring-indigo-500/50",
|
||||
"focus:border-transparent"
|
||||
)}
|
||||
/>
|
||||
|
|
@ -66,7 +66,7 @@ export function SearchInput({
|
|||
? "bg-indigo-600 text-white"
|
||||
: "bg-gray-50 dark:bg-gray-800 text-gray-600 dark:text-gray-400",
|
||||
"hover:bg-gray-100 dark:hover:bg-gray-700",
|
||||
"focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-opacity-50"
|
||||
"focus:outline-none focus:ring-2 focus:ring-indigo-500/50"
|
||||
)}
|
||||
>
|
||||
{filter.charAt(0).toUpperCase() + filter.slice(1)}
|
||||
|
|
|
|||
|
|
@ -99,8 +99,8 @@ export function ComposeBoxCopilot({
|
|||
autoResize={true}
|
||||
maxHeight={120}
|
||||
className={`
|
||||
!min-h-0
|
||||
!border-0 !shadow-none !ring-0
|
||||
min-h-0!
|
||||
border-0! shadow-none! ring-0!
|
||||
bg-transparent
|
||||
resize-none
|
||||
overflow-y-auto
|
||||
|
|
|
|||
|
|
@ -78,8 +78,8 @@ export function ComposeBoxPlayground({
|
|||
autoResize={true}
|
||||
maxHeight={120}
|
||||
className={`
|
||||
!min-h-0
|
||||
!border-0 !shadow-none !ring-0
|
||||
min-h-0!
|
||||
border-0! shadow-none! ring-0!
|
||||
bg-transparent
|
||||
resize-none
|
||||
overflow-y-auto
|
||||
|
|
|
|||
|
|
@ -93,8 +93,8 @@ export function ComposeBox({
|
|||
autoResize={true}
|
||||
maxHeight={120}
|
||||
className={`
|
||||
!min-h-0
|
||||
!border-0 !shadow-none !ring-0
|
||||
min-h-0!
|
||||
border-0! shadow-none! ring-0!
|
||||
bg-transparent
|
||||
resize-none
|
||||
overflow-y-auto
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ export function HelpModal({ isOpen, onClose, onStartTour }: HelpModalProps) {
|
|||
if (!isOpen) return null;
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black/50 backdrop-blur-sm z-[100] flex items-center justify-center">
|
||||
<div className="fixed inset-0 bg-black/50 backdrop-blur-sm z-100 flex items-center justify-center">
|
||||
<div className="bg-white dark:bg-zinc-800 rounded-lg shadow-lg p-6 w-[480px] max-w-[90vw] animate-in fade-in duration-200">
|
||||
<h2 className="text-xl font-semibold text-gray-900 dark:text-gray-100 mb-6">
|
||||
Need Help?
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ export function Panel({
|
|||
"flex flex-col overflow-hidden rounded-xl border relative",
|
||||
variant === 'copilot' ? "border-blue-200 dark:border-blue-800" : "border-zinc-200 dark:border-zinc-800",
|
||||
"bg-white dark:bg-zinc-900",
|
||||
maxHeight ? "max-h-[var(--panel-height)]" : "h-full",
|
||||
maxHeight ? "max-h-(--panel-height)" : "h-full",
|
||||
className
|
||||
)}
|
||||
style={{
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ function TourBackdrop({ targetElement }: { targetElement: Element | null }) {
|
|||
return (
|
||||
<>
|
||||
{/* Top */}
|
||||
<div className="fixed z-[100] backdrop-blur-sm bg-black/30" style={{
|
||||
<div className="fixed z-100 backdrop-blur-sm bg-black/30" style={{
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
|
|
@ -97,7 +97,7 @@ function TourBackdrop({ targetElement }: { targetElement: Element | null }) {
|
|||
}} />
|
||||
|
||||
{/* Left */}
|
||||
<div className="fixed z-[100] backdrop-blur-sm bg-black/30" style={{
|
||||
<div className="fixed z-100 backdrop-blur-sm bg-black/30" style={{
|
||||
top: Math.max(0, rect.top - padding),
|
||||
left: 0,
|
||||
width: Math.max(0, rect.left - padding),
|
||||
|
|
@ -105,7 +105,7 @@ function TourBackdrop({ targetElement }: { targetElement: Element | null }) {
|
|||
}} />
|
||||
|
||||
{/* Right */}
|
||||
<div className="fixed z-[100] backdrop-blur-sm bg-black/30" style={{
|
||||
<div className="fixed z-100 backdrop-blur-sm bg-black/30" style={{
|
||||
top: Math.max(0, rect.top - padding),
|
||||
left: rect.right + padding,
|
||||
right: 0,
|
||||
|
|
@ -113,7 +113,7 @@ function TourBackdrop({ targetElement }: { targetElement: Element | null }) {
|
|||
}} />
|
||||
|
||||
{/* Bottom */}
|
||||
<div className="fixed z-[100] backdrop-blur-sm bg-black/30" style={{
|
||||
<div className="fixed z-100 backdrop-blur-sm bg-black/30" style={{
|
||||
top: rect.bottom + padding,
|
||||
left: 0,
|
||||
right: 0,
|
||||
|
|
@ -122,7 +122,7 @@ function TourBackdrop({ targetElement }: { targetElement: Element | null }) {
|
|||
|
||||
{/* Highlight border around target */}
|
||||
<div
|
||||
className="fixed z-[100] border-2 border-white/50 rounded-lg pointer-events-none"
|
||||
className="fixed z-100 border-2 border-white/50 rounded-lg pointer-events-none"
|
||||
style={{
|
||||
top: rect.top - padding,
|
||||
left: rect.left - padding,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { useEffect, RefObject } from 'react';
|
||||
|
||||
export function useClickAway(
|
||||
ref: RefObject<HTMLElement>,
|
||||
ref: RefObject<HTMLElement | null>,
|
||||
handler: (event: MouseEvent | TouchEvent) => void
|
||||
) {
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,20 @@
|
|||
import { NextFetchEvent, NextRequest, NextResponse } from "next/server";
|
||||
import { withMiddlewareAuthRequired } from "@auth0/nextjs-auth0/edge";
|
||||
import { auth0 } from "./app/lib/auth0";
|
||||
|
||||
const corsOptions = {
|
||||
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
|
||||
'Access-Control-Allow-Headers': 'Content-Type, x-client-id, Authorization',
|
||||
}
|
||||
|
||||
const auth0MiddlewareHandler = withMiddlewareAuthRequired();
|
||||
|
||||
export async function middleware(request: NextRequest, event: NextFetchEvent) {
|
||||
const authRes = await auth0.middleware(request);
|
||||
|
||||
// Check if the request path starts with /api/auth/
|
||||
if (request.nextUrl.pathname.startsWith('/auth')) {
|
||||
return authRes;
|
||||
}
|
||||
|
||||
// Check if the request path starts with /api/
|
||||
if (request.nextUrl.pathname.startsWith('/api/')) {
|
||||
// Handle preflighted requests
|
||||
|
|
@ -37,10 +43,9 @@ export async function middleware(request: NextRequest, event: NextFetchEvent) {
|
|||
request.nextUrl.pathname.startsWith('/billing') ||
|
||||
request.nextUrl.pathname.startsWith('/onboarding')) {
|
||||
// Skip auth check if USE_AUTH is not enabled
|
||||
if (process.env.USE_AUTH !== 'true') {
|
||||
return NextResponse.next();
|
||||
if (process.env.USE_AUTH === 'true') {
|
||||
return authRes;
|
||||
}
|
||||
return auth0MiddlewareHandler(request, event);
|
||||
}
|
||||
|
||||
return NextResponse.next();
|
||||
|
|
@ -48,10 +53,12 @@ export async function middleware(request: NextRequest, event: NextFetchEvent) {
|
|||
|
||||
export const config = {
|
||||
matcher: [
|
||||
'/projects/:path*',
|
||||
'/billing/:path*',
|
||||
// '/onboarding/:path*',
|
||||
'/api/v1/:path*',
|
||||
'/api/widget/v1/:path*',
|
||||
/*
|
||||
* Match all request paths except for the ones starting with:
|
||||
* - _next/static (static files)
|
||||
* - _next/image (image optimization files)
|
||||
* - favicon.ico, sitemap.xml, robots.txt (metadata files)
|
||||
*/
|
||||
"/((?!_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)",
|
||||
],
|
||||
};
|
||||
};
|
||||
15613
apps/rowboat/package-lock.json
generated
15613
apps/rowboat/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -4,7 +4,7 @@
|
|||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"dev": "next dev --turbopack",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@ai-sdk/openai": "^1.3.21",
|
||||
"@auth0/nextjs-auth0": "^3.5.0",
|
||||
"@auth0/nextjs-auth0": "^4.7.0",
|
||||
"@aws-sdk/client-s3": "^3.743.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.743.0",
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
|
|
@ -25,14 +25,14 @@
|
|||
"@floating-ui/react": "^0.27.7",
|
||||
"@google/generative-ai": "^0.21.0",
|
||||
"@heroicons/react": "^2.2.0",
|
||||
"@heroui/react": "2.7.4",
|
||||
"@heroui/system": "2.4.11",
|
||||
"@heroui/theme": "2.4.11",
|
||||
"@heroui/react": "^2.8.0-beta.10",
|
||||
"@heroui/system": "^2.4.18-beta.2",
|
||||
"@heroui/theme": "^2.4.18-beta.2",
|
||||
"@langchain/core": "^0.3.7",
|
||||
"@langchain/textsplitters": "^0.1.0",
|
||||
"@mendable/firecrawl-js": "^1.0.3",
|
||||
"@modelcontextprotocol/sdk": "^1.12.1",
|
||||
"@primer/react": "^36.27.0",
|
||||
"@primer/react": "^37.27.0",
|
||||
"@qdrant/js-client-rest": "^1.13.0",
|
||||
"ai": "^4.3.13",
|
||||
"cheerio": "^1.0.0",
|
||||
|
|
@ -41,24 +41,24 @@
|
|||
"date-fns": "^4.1.0",
|
||||
"dotenv": "^16.4.5",
|
||||
"eventsource-parser": "^3.0.2",
|
||||
"framer-motion": "^11.5.4",
|
||||
"framer-motion": "^12.19.1",
|
||||
"fuse.js": "^7.1.0",
|
||||
"immer": "^10.1.1",
|
||||
"jose": "^5.9.6",
|
||||
"lucide-react": "^0.465.0",
|
||||
"mongodb": "^6.8.0",
|
||||
"next": "^14.2.25",
|
||||
"next": "15.3.4",
|
||||
"openai": "^4.67.2",
|
||||
"quill": "^2.0.3",
|
||||
"quill-mention": "^6.0.2",
|
||||
"react": "^18.3.1",
|
||||
"react-diff-viewer-continued": "^3.4.0",
|
||||
"react-dom": "^18.3.1",
|
||||
"react": "19.1.0",
|
||||
"react-diff-viewer-continued": "^4.0.6",
|
||||
"react-dom": "19.1.0",
|
||||
"react-dropzone": "^14.3.5",
|
||||
"react-markdown": "^9.0.1",
|
||||
"react-markdown": "^10.1.0",
|
||||
"react-resizable-panels": "^2.1.7",
|
||||
"redis": "^4.7.0",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"rowboat-shared": "github:rowboatlabs/shared",
|
||||
"sharp": "^0.33.4",
|
||||
"styled-components": "^5.3.11",
|
||||
|
|
@ -67,20 +67,24 @@
|
|||
"tailwindcss-animate": "^1.0.7",
|
||||
"tiktoken": "^1.0.17",
|
||||
"twilio": "^5.4.5",
|
||||
"typewriter-effect": "^2.21.0",
|
||||
"zod": "^3.23.8",
|
||||
"zod-to-json-schema": "^3.23.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4.1.10",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"@types/react": "19.1.8",
|
||||
"@types/react-dom": "19.1.6",
|
||||
"@types/redis": "^4.0.11",
|
||||
"eslint": "^8",
|
||||
"eslint-config-next": "14.2.5",
|
||||
"postcss": "^8",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"eslint-config-next": "15.3.4",
|
||||
"postcss": "^8.5.6",
|
||||
"tailwindcss": "^4.1.10",
|
||||
"tsx": "^4.19.1",
|
||||
"typescript": "^5"
|
||||
},
|
||||
"overrides": {
|
||||
"@types/react": "19.1.8",
|
||||
"@types/react-dom": "19.1.6"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
/** @type {import('postcss-load-config').Config} */
|
||||
const config = {
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
'@tailwindcss/postcss': {},
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
}
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
import { heroui } from "@heroui/theme";
|
||||
import type { Config } from "tailwindcss";
|
||||
|
||||
const config: Config = {
|
||||
darkMode: ["class"],
|
||||
content: [
|
||||
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./components/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./app/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./node_modules/@heroui/theme/dist/components/[object Object].js",
|
||||
"./node_modules/@heroui/theme/dist/**/*.{js,ts,jsx,tsx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
keyframes: {
|
||||
shine: {
|
||||
'100%': { transform: 'translateX(200%)' }
|
||||
},
|
||||
'pulse-subtle': {
|
||||
'0%, 100%': { opacity: '1' },
|
||||
'50%': { opacity: '0.85' }
|
||||
},
|
||||
gradient: {
|
||||
'0%': { backgroundPosition: '0% 50%' },
|
||||
'50%': { backgroundPosition: '100% 50%' },
|
||||
'100%': { backgroundPosition: '0% 50%' }
|
||||
},
|
||||
'sparkle-fade': {
|
||||
'0%': { opacity: '0.2', transform: 'scale(0.9)' },
|
||||
'50%': { opacity: '0.5', transform: 'scale(1.1)' },
|
||||
'100%': { opacity: '0.2', transform: 'scale(0.9)' }
|
||||
},
|
||||
typing: {
|
||||
'0%, 5%': { width: '0%' },
|
||||
'45%, 55%': { width: '100%' },
|
||||
'95%, 100%': { width: '0%' }
|
||||
},
|
||||
blink: {
|
||||
'50%': { borderColor: 'transparent' }
|
||||
}
|
||||
},
|
||||
animation: {
|
||||
shine: 'shine 2s infinite',
|
||||
'pulse-subtle': 'pulse-subtle 2s infinite',
|
||||
'gradient': 'gradient var(--gradient-animation-duration, 15s) ease infinite',
|
||||
'sparkle': 'sparkle-fade 4s cubic-bezier(0.4, 0, 0.6, 1) infinite',
|
||||
'typing': 'typing 8s cubic-bezier(0.4, 0, 0.2, 1) infinite',
|
||||
'cursor': 'blink .75s step-end infinite'
|
||||
},
|
||||
backgroundImage: {
|
||||
'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
|
||||
'gradient-conic': 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))'
|
||||
},
|
||||
borderRadius: {
|
||||
lg: 'var(--radius)',
|
||||
md: 'calc(var(--radius) - 2px)',
|
||||
sm: 'calc(var(--radius) - 4px)'
|
||||
},
|
||||
colors: {
|
||||
background: 'hsl(var(--background))',
|
||||
foreground: 'hsl(var(--foreground))',
|
||||
card: {
|
||||
DEFAULT: 'hsl(var(--card))',
|
||||
foreground: 'hsl(var(--card-foreground))'
|
||||
},
|
||||
popover: {
|
||||
DEFAULT: 'hsl(var(--popover))',
|
||||
foreground: 'hsl(var(--popover-foreground))'
|
||||
},
|
||||
primary: {
|
||||
DEFAULT: 'hsl(var(--primary))',
|
||||
foreground: 'hsl(var(--primary-foreground))'
|
||||
},
|
||||
secondary: {
|
||||
DEFAULT: 'hsl(var(--secondary))',
|
||||
foreground: 'hsl(var(--secondary-foreground))'
|
||||
},
|
||||
muted: {
|
||||
DEFAULT: 'hsl(var(--muted))',
|
||||
foreground: 'hsl(var(--muted-foreground))'
|
||||
},
|
||||
accent: {
|
||||
DEFAULT: 'hsl(var(--accent))',
|
||||
foreground: 'hsl(var(--accent-foreground))'
|
||||
},
|
||||
destructive: {
|
||||
DEFAULT: 'hsl(var(--destructive))',
|
||||
foreground: 'hsl(var(--destructive-foreground))'
|
||||
},
|
||||
border: 'hsl(var(--border))',
|
||||
input: 'hsl(var(--input))',
|
||||
ring: 'hsl(var(--ring))',
|
||||
chart: {
|
||||
'1': 'hsl(var(--chart-1))',
|
||||
'2': 'hsl(var(--chart-2))',
|
||||
'3': 'hsl(var(--chart-3))',
|
||||
'4': 'hsl(var(--chart-4))',
|
||||
'5': 'hsl(var(--chart-5))'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
heroui(),
|
||||
require("tailwindcss-animate")
|
||||
],
|
||||
};
|
||||
export default config;
|
||||
|
|
@ -1,6 +1,10 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
|
|
@ -18,9 +22,19 @@
|
|||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": ["./*"]
|
||||
}
|
||||
"@/*": [
|
||||
"./*"
|
||||
]
|
||||
},
|
||||
"target": "ES2017"
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue