Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ _worker.bundle
Modelfile
modelfiles

# react router
.react-router/

# docs ignore
site

Expand Down
37 changes: 0 additions & 37 deletions app/.client/components/AuthErrorToast.client.tsx

This file was deleted.

7 changes: 3 additions & 4 deletions app/.client/components/Home.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,27 @@
import * as Tooltip from '@radix-ui/react-tooltip';
import { useNavigate } from '@remix-run/react';
import { generateId } from 'ai';
import classNames from 'classnames';
import { AnimatePresence, motion, useAnimate } from 'framer-motion';
import { startTransition, useState } from 'react';
import { useNavigate, useNavigation } from 'react-router';
import { ChatTextarea } from './chat/ChatTextarea';
import { ExamplePrompts } from './chat/ExamplePrompts';
import FilePreview from './chat/FilePreview';
import { ScreenshotStateManager } from './chat/ScreenshotStateManager';

export function Home({ className }: { className?: string }) {
const navigate = useNavigate();
const navigation = useNavigation();
const isNavigating = Boolean(navigation.location);

const [animationScope] = useAnimate();
const [uploadFiles, setUploadFiles] = useState<File[]>([]);
const [isNavigating, setIsNavigating] = useState(false);

const handleSendMessage = (message?: string) => {
if (!message) {
return;
}

setIsNavigating(true);

const id = generateId();

startTransition(() => {
Expand Down
2 changes: 1 addition & 1 deletion app/.client/components/auth/AuthButtons.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useNavigate } from '@remix-run/react';
import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router';
import { Button } from '~/.client/components/ui/Button';
import { useAuth } from '~/.client/hooks/useAuth';

Expand Down
13 changes: 6 additions & 7 deletions app/.client/components/chat/Chat.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import type { Route } from '.react-router/types/app/routes/+types/chat';
import { useStore } from '@nanostores/react';
import * as Tooltip from '@radix-ui/react-tooltip';
import { useLoaderData, useLocation, useNavigate } from '@remix-run/react';
import classNames from 'classnames';
import { useAnimate } from 'framer-motion';
import { useEffect, useRef, useState } from 'react';
import { useLocation, useNavigate } from 'react-router';
import { useSnapScroll } from '~/.client/hooks';
import { useChatMessage } from '~/.client/hooks/useChatMessage';
import { aiState, setChatId, setChatStarted } from '~/.client/stores/ai-state';
import { webBuilderStore } from '~/.client/stores/web-builder';
import { renderLogger } from '~/.client/utils/logger';
import type { ChatMessage, ChatWithMessages } from '~/types/chat';
import type { ChatMessage } from '~/types/chat';
import { WebBuilder } from '../webbuilder/WebBuilder';
import styles from './BaseChat.module.scss';
import ChatAlert from './ChatAlert';
Expand All @@ -24,12 +24,11 @@ export type ImageData = {
base64?: string;
};

export function Chat({ className }: { className?: string }) {
renderLogger.trace('Chat');
export function Chat({ loaderData, className }: Route.ComponentProps & { className?: string }) {
const { id, chat } = loaderData;

const location = useLocation();
const locationState = location.state as { message?: string; files?: File[] };

const { id, chat } = useLoaderData<{ id: string; chat?: ChatWithMessages }>();
const navigate = useNavigate();
const { showChat } = useStore(aiState);
const actionAlert = useStore(webBuilderStore.chatStore.alert);
Expand Down
2 changes: 1 addition & 1 deletion app/.client/components/chat/Messages.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { useStore } from '@nanostores/react';
import { useLocation } from '@remix-run/react';
import classNames from 'classnames';
import type { ForwardedRef } from 'react';
import { Fragment, forwardRef, memo, useEffect, useMemo, useRef } from 'react';
import { useLocation } from 'react-router';
import { toast } from 'sonner';
import WithTooltip from '~/.client/components/ui/Tooltip';
import { useAuth } from '~/.client/hooks/useAuth';
Expand Down
2 changes: 1 addition & 1 deletion app/.client/components/header/HeaderActionButtons.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { useStore } from '@nanostores/react';
import { useFetcher } from '@remix-run/react';
import classNames from 'classnames';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { renderToStaticMarkup } from 'react-dom/server';
import { useFetcher } from 'react-router';
import { toast } from 'sonner';
import { NetlifyDeploymentLink } from '~/.client/components/chat/NetlifyDeploymentLink.client';
import useViewport from '~/.client/hooks';
Expand Down
2 changes: 1 addition & 1 deletion app/.client/components/header/MinimalAvatarDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export const MinimalAvatarDropdown = ({}: MinimalAvatarDropdownProps) => {
return 'Guest User';
}

return userInfo.name || userInfo.username;
return userInfo.name || userInfo.username || '';
}, [userInfo]);

const contactInfo = useMemo(() => {
Expand Down
6 changes: 3 additions & 3 deletions app/.client/components/header/PushToGitHubDialog/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import type { Route } from '.react-router/types/app/+types/root';
import { useStore } from '@nanostores/react';
import { useFetcher, useRouteLoaderData } from '@remix-run/react';
import { useEffect, useMemo, useState } from 'react';
import { useFetcher, useRouteLoaderData } from 'react-router';
import { toast } from 'sonner';
import { aiState } from '~/.client/stores/ai-state';
import { githubConnection, updateGitHubConnection } from '~/.client/stores/github';
import { webBuilderStore } from '~/.client/stores/web-builder';
import { logger } from '~/.client/utils/logger';
import type { ConnectionSettings } from '~/root';
import type { ApiResponse } from '~/types/global';
import { DialogContainer } from './DialogContainer';
import { GitHubConnectionView } from './GitHubConnectionView';
Expand All @@ -19,7 +19,7 @@ interface PushToGitHubDialogProps {
}

export function PushToGitHubDialog({ isOpen, onClose }: PushToGitHubDialogProps) {
const rootData = useRouteLoaderData<{ connectionSettings?: ConnectionSettings }>('root');
const rootData = useRouteLoaderData<Route.ComponentProps['loaderData']>('root');
const isGitHubConfigured = rootData?.connectionSettings?.githubConnection || false;
const connection = useStore(githubConnection);
const { chatId } = useStore(aiState);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useStore } from '@nanostores/react';
import { useRevalidator } from '@remix-run/react';
import classNames from 'classnames';
import { useEffect, useState } from 'react';
import { useRevalidator } from 'react-router';
import { toast } from 'sonner';
import { Button } from '~/.client/components/ui/Button';
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '~/.client/components/ui/Collapsible';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import type { Route } from '.react-router/types/app/+types/root';
import { useStore } from '@nanostores/react';
import { useFetcher, useRouteLoaderData } from '@remix-run/react';
import classNames from 'classnames';
import { formatDistanceToNow } from 'date-fns';
import { zhCN } from 'date-fns/locale/zh-CN';
import { useEffect, useMemo, useState } from 'react';
import { useFetcher, useRouteLoaderData } from 'react-router';
import { toast } from 'sonner';
import { Badge } from '~/.client/components/ui/Badge';
import { Button } from '~/.client/components/ui/Button';
Expand All @@ -15,7 +16,6 @@ import {
updateNetlifyConnection,
} from '~/.client/stores/netlify';
import { logger } from '~/.client/utils/logger';
import type { ConnectionSettings } from '~/root';
import type { ApiResponse } from '~/types/global';
import type { NetlifyBuild, NetlifyDeploy, NetlifySite } from '~/types/netlify';
import ConnectionBorder from './components/ConnectionBorder';
Expand All @@ -30,7 +30,7 @@ interface SiteAction {
}

export default function NetlifyConnection() {
const rootData = useRouteLoaderData<{ connectionSettings?: ConnectionSettings }>('root');
const rootData = useRouteLoaderData<Route.ComponentProps['loaderData']>('root');
const connectFetcher = useFetcher<ApiResponse>();
const settingsFetcher = useFetcher<ApiResponse>();

Expand Down Expand Up @@ -112,7 +112,7 @@ export default function NetlifyConnection() {
try {
setIsActionLoading(true);

const response = await fetch(`/api/netlify/deploys/${deployId}/${action}`, {
const response = await fetch(`/api/netlify/actions/${deployId}/${action}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import type { Route } from '.react-router/types/app/+types/root';
import { useStore } from '@nanostores/react';
import { useFetcher, useRouteLoaderData } from '@remix-run/react';
import classNames from 'classnames';
import React, { useEffect, useMemo, useState } from 'react';
import { useFetcher, useRouteLoaderData } from 'react-router';
import { toast } from 'sonner';
import { logStore } from '~/.client/stores/logs';
import { fetchVercelStats, isFetchingStats, updateVercelConnection, vercelConnection } from '~/.client/stores/vercel';
import { logger } from '~/.client/utils/logger';
import type { ConnectionSettings } from '~/root';
import ConnectionBorder from './components/ConnectionBorder';

interface ApiResponse {
Expand All @@ -16,7 +16,7 @@ interface ApiResponse {
}

export default function VercelConnection() {
const rootData = useRouteLoaderData<{ connectionSettings?: ConnectionSettings }>('root');
const rootData = useRouteLoaderData<Route.ComponentProps['loaderData']>('root');
const settingsFetcher = useFetcher<ApiResponse>();
const connectFetcher = useFetcher<ApiResponse>();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import type { Route } from '.react-router/types/app/+types/root';
import { useStore } from '@nanostores/react';
import { useFetcher, useRouteLoaderData } from '@remix-run/react';
import classNames from 'classnames';
import { format, formatDistanceToNow } from 'date-fns';
import { zhCN } from 'date-fns/locale/zh-CN';
import { motion } from 'framer-motion';
import React, { useEffect, useMemo, useState } from 'react';
import { useFetcher, useRouteLoaderData } from 'react-router';
import { toast } from 'sonner';
import { Badge } from '~/.client/components/ui/Badge';
import { Button } from '~/.client/components/ui/Button';
Expand All @@ -16,7 +17,6 @@ import {
update1PanelConnection,
} from '~/.client/stores/1panel';
import { getChatId } from '~/.client/stores/ai-state';
import type { ConnectionSettings } from '~/root';
import type { _1PanelWebsite } from '~/types/1panel';
import type { ApiResponse } from '~/types/global';
import ConnectionBorder from './components/ConnectionBorder';
Expand All @@ -28,7 +28,7 @@ export default function _1PanelConnection({
isDeploying: boolean;
onDeploy: (siteId: number) => void;
}) {
const rootData = useRouteLoaderData<{ connectionSettings?: ConnectionSettings }>('root');
const rootData = useRouteLoaderData<Route.ComponentProps['loaderData']>('root');
const connectFetcher = useFetcher<ApiResponse>();
const settingsFetcher = useFetcher<ApiResponse>();

Expand Down
2 changes: 1 addition & 1 deletion app/.client/components/sidebar/HistoryItem.client.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useParams } from '@remix-run/react';
import classNames from 'classnames';
import { type ForwardedRef, forwardRef, useCallback } from 'react';
import { useParams } from 'react-router';
import { Checkbox } from '~/.client/components/ui/Checkbox';
import WithTooltip from '~/.client/components/ui/Tooltip';
import { useEditChatDescription } from '~/.client/hooks';
Expand Down
2 changes: 0 additions & 2 deletions app/.client/components/ui/Badge.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
'use client';

import { cva, type VariantProps } from 'class-variance-authority';
import classNames from 'classnames';
import * as React from 'react';
Expand Down
2 changes: 0 additions & 2 deletions app/.client/components/ui/Collapsible.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
'use client';

import * as CollapsiblePrimitive from '@radix-ui/react-collapsible';

const Collapsible = CollapsiblePrimitive.Root;
Expand Down
18 changes: 4 additions & 14 deletions app/.client/hooks/useAuth.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,11 @@
import { useFetcher, useRouteLoaderData } from '@remix-run/react';
/*
* 用户认证Hook
*/
import type { Route } from '.react-router/types/app/+types/root';
import { useCallback, useEffect, useState } from 'react';
import { useFetcher, useRouteLoaderData } from 'react-router';

export interface UserInfo {
sub?: string;
name?: string;
// 用户登录名,如果未启用用户名登录则可能为空
username?: string;
picture?: string;
// 用户邮箱,可能为空
email?: string;
// 用户手机号,可能为空
phone_number?: string;
[key: string]: any;
}
type UserInfo = Route.ComponentProps['loaderData']['auth']['userInfo'];

interface AuthUserResponse {
isAuthenticated: boolean;
Expand All @@ -29,7 +19,7 @@ interface AuthUserResponse {
*/
export function useAuth() {
// 尝试从根加载器获取数据
const rootData = useRouteLoaderData<{ auth?: { isAuthenticated: boolean; userInfo: UserInfo | null } }>('root');
const rootData = useRouteLoaderData<Route.ComponentProps['loaderData']>('root');

const [isAuthenticated, setIsAuthenticated] = useState<boolean>(rootData?.auth?.isAuthenticated || false);
const [userInfo, setUserInfo] = useState<UserInfo | null>(rootData?.auth?.userInfo || null);
Expand Down
5 changes: 3 additions & 2 deletions app/.client/hooks/useChatDeployment.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import type { Route } from '.react-router/types/app/routes/+types/chat';
import type { Deployment } from '@prisma/client';
import { useRouteLoaderData } from '@remix-run/react';
import { useCallback, useMemo } from 'react';
import { useRouteLoaderData } from 'react-router';
import type { DeploymentPlatform } from '~/types/deployment';

/**
* 获取 Chat 路由中的部署记录
*/
export function useChatDeployment() {
const chatRouteData = useRouteLoaderData<{ deployments?: Deployment[] }>('routes/_layout.chat.$id');
const chatRouteData = useRouteLoaderData<Route.ComponentProps['loaderData']>('chat');
const deployments = useMemo(() => {
if (chatRouteData?.deployments) {
return chatRouteData.deployments;
Expand Down
2 changes: 1 addition & 1 deletion app/.client/hooks/useChatEntries.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useFetcher } from '@remix-run/react';
import { useCallback, useEffect, useState } from 'react';
import { useFetcher } from 'react-router';
import { debounce } from '~/.client/utils/debounce';
import type { ApiResponse } from '~/types/global';

Expand Down
6 changes: 3 additions & 3 deletions app/.client/hooks/useChatHistory.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useRouteLoaderData, useSearchParams } from '@remix-run/react';
import type { Route } from '.react-router/types/app/routes/+types/chat';
import { useCallback } from 'react';
import { useRouteLoaderData, useSearchParams } from 'react-router';
import type { Section } from '~/types/actions';
import type { ChatWithMessages } from '~/types/chat';
import type { PageAssetData, PageData } from '~/types/pages';
import { useEditorStorage } from '../persistence/editor';

Expand All @@ -13,7 +13,7 @@ export interface ProjectData {
}

export function useChatHistory() {
const routeData = useRouteLoaderData<{ chat?: ChatWithMessages }>('routes/_layout.chat.$id');
const routeData = useRouteLoaderData<Route.ComponentProps['loaderData']>('chat');
if (!routeData) {
return;
}
Expand Down
2 changes: 1 addition & 1 deletion app/.client/hooks/useChatMessage.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useChat } from '@ai-sdk/react';
import { useSearchParams } from '@remix-run/react';
import { DefaultChatTransport, type FileUIPart } from 'ai';
import { useEffect, useMemo, useState } from 'react';
import { useSearchParams } from 'react-router';
import { toast } from 'sonner';
import { createScopedLogger } from '~/.client/utils/logger';
import { pagesToArtifacts } from '~/.client/utils/page';
Expand Down
2 changes: 1 addition & 1 deletion app/.client/hooks/useChatOperate.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useFetcher, useNavigate } from '@remix-run/react';
import { useCallback } from 'react';
import { useFetcher, useNavigate } from 'react-router';
import { toast } from 'sonner';
import {
deleteEditorProject,
Expand Down
Loading