Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
9057139
add cosine
gofenix Jun 8, 2024
4e03e1a
[Deno Deploy] Add .github/workflows/deploy.yml
deno-deploy[bot] Jun 8, 2024
dfd5599
update to next15
gofenix Jun 8, 2024
e910401
add try catch
gofenix Jun 8, 2024
a1384c7
add login
gofenix Jun 9, 2024
e92937f
add login
gofenix Jun 9, 2024
3950fe6
remove deno deploy
gofenix Jun 9, 2024
f60eb2f
Merge branch 'main' into cosine
gofenix Jun 9, 2024
325c5c9
merge main
gofenix Jun 9, 2024
394d7ba
update package
gofenix Jun 9, 2024
e91cdb9
add used
gofenix Jun 9, 2024
8b3df8f
add header
gofenix Jun 10, 2024
f0b312b
add related used
gofenix Jun 10, 2024
547dc77
update logo and prompt
gofenix Jun 10, 2024
fa93305
update sign error
gofenix Jun 10, 2024
ca508b8
update image
gofenix Jun 11, 2024
eea2b1e
Update query-suggestor.ts
owenlsatwinner Jun 11, 2024
ec6beb5
Update query-suggestor.ts
owenlsatwinner Jun 11, 2024
b85cdb0
Update researcher.ts
owenlsatwinner Jun 11, 2024
151adc2
Update task-manager.ts
owenlsatwinner Jun 11, 2024
1655b20
Update writer.tsx
owenlsatwinner Jun 11, 2024
ec02f8b
add tavily search log
gofenix Jun 11, 2024
0acc05d
Merge branch 'main' into cosine
gofenix Jun 14, 2024
73b3e49
remove click to cosine
gofenix Jun 14, 2024
e483882
use Chinese
gofenix Jun 14, 2024
cb4faf9
use home icon
gofenix Jun 14, 2024
d593384
support mobile
gofenix Jun 14, 2024
98ba68a
use 1.13
gofenix Jun 14, 2024
94b90a9
add color
gofenix Jun 17, 2024
397fcde
Merge branch 'main' into cosine
gofenix Jun 17, 2024
f8c9059
update help jpeg
gofenix Jun 22, 2024
54c455f
add logo
gofenix Jun 22, 2024
83a67e9
add guest login
gofenix Jun 22, 2024
447c488
Merge branch 'main' into cosine
gofenix Jun 22, 2024
58cbeb5
merge main
gofenix Jun 22, 2024
54f1a04
add some dep
gofenix Jun 23, 2024
cf33ee1
Merge branch 'main' into cosine
gofenix Jun 24, 2024
45ac67e
add sponsor
gofenix Jul 6, 2024
4acd0ad
add animate
gofenix Jul 6, 2024
84a3492
make optional for input
gofenix Jul 10, 2024
9db0cba
Merge branch 'main' into cosine
gofenix Jul 20, 2024
9e6c1d4
update afdian.com
gofenix Jul 21, 2024
537687a
Merge branch 'main' into cosine
gofenix Aug 11, 2024
b5908d8
add coze
gofenix Aug 11, 2024
f05680a
Merge branch 'main' into cosine
gofenix Sep 5, 2024
04f45dc
update bun
gofenix Sep 5, 2024
9742d78
update package
gofenix Sep 5, 2024
08fa973
add package
gofenix Sep 5, 2024
e794122
remove unsed icon
gofenix Sep 6, 2024
28e1447
Merge branch 'main' into cosine
gofenix Nov 10, 2024
4cc5ed0
fix build error
gofenix Nov 11, 2024
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
4 changes: 4 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.github/
.next/
.vscode/
node_modules/
15 changes: 15 additions & 0 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,18 @@
@apply bg-background text-foreground;
}
}

.animate-in {
animation: animateIn 0.3s ease 0.15s both;
}

@keyframes animateIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
24 changes: 13 additions & 11 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,36 @@ import { Inter as FontSans } from 'next/font/google'
import './globals.css'
import { cn } from '@/lib/utils'
import { ThemeProvider } from '@/components/theme-provider'
import Header from '@/components/header'
import Footer from '@/components/footer'
import Header from '@/components/header-cosine'
import Footer from '@/components/footer-cosine'
import { Sidebar } from '@/components/sidebar'
import { Toaster } from '@/components/ui/sonner'
import { AppStateProvider } from '@/lib/utils/app-state'
import CozeBot from '@/components/coze'

const fontSans = FontSans({
subsets: ['latin'],
variable: '--font-sans'
})

const title = 'Morphic'
const title = '余弦法律:您的AI分析引擎,智能法律助手'
const description =
'A fully open-source AI-powered answer engine with a generative UI.'
'通过新一代人工智能(Agent),为律师和当事人提供前所未有的法律服务体验。我们的平台通过深入分析、智能检索和精准推荐,有效辅助法律从业者减少40%日常工作量。'

export const metadata: Metadata = {
metadataBase: new URL('https://morphic.sh'),
metadataBase: new URL('https://cosine.work'),
title,
description,
openGraph: {
title,
description
},
twitter: {
title,
description,
card: 'summary_large_image',
creator: '@miiura'
}
// twitter: {
// title,
// description,
// card: 'summary_large_image',
// creator: '@miiura'
// }
}

export const viewport: Viewport = {
Expand Down Expand Up @@ -61,6 +62,7 @@ export default function RootLayout({
<Sidebar />
<Footer />
<Toaster />
<CozeBot botId="7394086744209391651" bottom="40px" />
</AppStateProvider>
</ThemeProvider>
</body>
Expand Down
135 changes: 135 additions & 0 deletions app/login/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import { redirect } from 'next/navigation'
import { SubmitButton } from './submit-button'
import { headers } from 'next/headers'
import { createClient } from '@/utils/supabase/server'
import Image from 'next/image'

export default function Login({
searchParams
}: {
searchParams: { message: string }
}) {
const signIn = async (formData: FormData) => {
'use server'

const email = formData.get('email') as string
const password = formData.get('password') as string

const supabase = createClient()
const { error } = await supabase.auth.signInWithPassword({
email,
password
})

if (error) {
return redirect(
`/login?message=${encodeURIComponent('用户名或密码错误')}`
)
}

return redirect('/')
}

const signInByGuest = async (formData: FormData) => {
'use server'

const supabase = createClient()
const { error } = await supabase.auth.signInAnonymously()

if (error) {
return redirect(
`/login?message=${encodeURIComponent('用户名或密码错误')}`
)
}

return redirect('/')
}

const signUp = async (formData: FormData) => {
'use server'

const origin = headers().get('origin')
const email = formData.get('email') as string
const password = formData.get('password') as string

const supabase = createClient()
const { error } = await supabase.auth.signUp({
email,
password,
options: {
emailRedirectTo: `${origin}/auth/callback`
}
})

if (error) {
return redirect(
`/login?message=${encodeURIComponent('注册失败,请换一个邮箱重试')}`
)
}

return redirect('/')
}

return (
<div className="bg-background text-foreground min-h-screen flex flex-col items-center">
<div className="flex-1 flex flex-col w-full px-8 sm:max-w-md justify-center gap-2">
<form className="animate-in flex-1 flex flex-col w-full justify-center gap-2 text-foreground">
<h1 className="animate-in text-slate-700 font-bold text-2xl mb-12 flex items-center gap-3 dark:text-slate-400">
<Image
src={'/logo.png'}
width="32"
height="32"
alt="MagickPen logo"
/>
余弦法律:AI全网分析引擎
</h1>
<label className="text-md font-bold" htmlFor="email">
邮箱
</label>
<input
className="rounded-md px-4 py-2 bg-inherit border mb-6"
name="email"
placeholder="you@example.com"
required={false}
/>
<label className="text-md font-bold" htmlFor="password">
密码
</label>
<input
className="rounded-md px-4 py-2 bg-inherit border mb-6"
type="password"
name="password"
placeholder="••••••••"
required={false}
/>
<SubmitButton
formAction={signIn}
className="bg-green-700 rounded-md px-4 py-2 text-foreground mb-2"
pendingText="登录中..."
>
登录
</SubmitButton>
<SubmitButton
formAction={signUp}
className="border border-foreground/20 rounded-md px-4 py-2 text-foreground mb-2"
pendingText="注册中..."
>
注册
</SubmitButton>
<SubmitButton
formAction={signInByGuest}
className="border border-foreground/20 rounded-md px-4 py-2 text-foreground mb-2"
pendingText="登录中..."
>
游客登录
</SubmitButton>
{searchParams?.message && (
<p className="mt-4 p-4 bg-foreground/10 text-foreground text-center">
{searchParams.message}
</p>
)}
</form>
</div>
</div>
)
}
20 changes: 20 additions & 0 deletions app/login/submit-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"use client";

import { useFormStatus } from "react-dom";
import { type ComponentProps } from "react";

type Props = ComponentProps<"button"> & {
pendingText?: string;
};

export function SubmitButton({ children, pendingText, ...props }: Props) {
const { pending, action } = useFormStatus();

const isPending = pending && action === props.formAction;

return (
<button {...props} type="submit" aria-disabled={pending}>
{isPending ? pendingText : children}
</button>
);
}
Binary file modified bun.lockb
Binary file not shown.
45 changes: 45 additions & 0 deletions components/auth-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { createClient } from '@/utils/supabase/server'
import Link from 'next/link'
import { redirect } from 'next/navigation'
import { Button } from './ui/button'

export default async function AuthButton() {
const supabase = createClient()
const {
data: { user }
} = await supabase.auth.getUser()

const signOut = async () => {
'use server'
const supabase = createClient()

await supabase.auth.signOut()
return redirect('/login')
}

return user ? (
<div className="text-sm flex items-center gap-2 w-full">
<span className="text-muted-foreground ">
{user.email ? user.email : '游客'}
</span>
<form action={signOut}>
<Button
variant="ghost"
size="icon"
className="text-muted-foreground/50 hidden md:block"
>
登出
</Button>
</form>
</div>
) : (
<Button variant="ghost" size="icon" className="w-full">
<Link
href="/login"
className="py-4 px-2 text-sm flex rounded-md no-underline bg-btn-background hover:bg-btn-background-hover"
>
注册/登录
</Link>
</Button>
)
}
52 changes: 50 additions & 2 deletions components/chat-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ import { EmptyScreen } from './empty-screen'
import Textarea from 'react-textarea-autosize'
import { generateId } from 'ai'
import { useAppState } from '@/lib/utils/app-state'
import Image from 'next/image'
import MoreTools from './more-tool'
import { createClient } from '@/utils/supabase/client'
import { ModelSelector } from './model-selector'
import { models } from '@/lib/types/models'
import { useLocalStorage } from '@/lib/hooks/use-local-storage'
Expand Down Expand Up @@ -85,6 +88,39 @@ export function ChatPanel({ messages, query, onModelChange }: ChatPanelProps) {
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
const formData = new FormData(e.currentTarget)
await handleQuerySubmit(input, formData)

const supabase = createClient()
const {
data: { user }
} = await supabase.auth.getUser()
if (!user) {
alert('请先使用登录使用!')
router.push('/login')
return
}
if (user) {
await supabase
.from('morphic_used')
.insert([
{
user_id: user.id,
email: user.email,
send_message: input
}
])
.select()
}
if (user.is_anonymous) {
const { data } = await supabase
.from('morphic_used')
.select()
.eq('user_id', user.id)

if (data?.length && data.length > 3) {
alert('游客试用已结束,请绑定邮箱使用!')
router.push('/login')
}
try {
await handleQuerySubmit(input, formData)
} catch (error) {
Expand All @@ -94,6 +130,7 @@ export function ChatPanel({ messages, query, onModelChange }: ChatPanelProps) {
handleClear()
}
}
}

// if query is not empty, submit the query
useEffect(() => {
Expand Down Expand Up @@ -137,7 +174,7 @@ export function ChatPanel({ messages, query, onModelChange }: ChatPanelProps) {
disabled={isGenerating}
>
<span className="text-sm mr-2 group-hover:block hidden animate-in fade-in duration-300">
New
新建话题
</span>
<Plus size={18} className="group-hover:rotate-90 transition-all" />
</Button>
Expand All @@ -155,6 +192,16 @@ export function ChatPanel({ messages, query, onModelChange }: ChatPanelProps) {
'fixed bottom-8 left-0 right-0 top-10 mx-auto h-screen flex flex-col items-center justify-center'
}
>
<h1 className="animate-in text-slate-700 font-bold text-2xl mb-12 flex items-center gap-3 dark:text-slate-400">
<Image
src={'/logo.png'}
className="animate-bounce"
width="32"
height="32"
alt="MagickPen logo"
/>
余弦法律:AI全网分析引擎
</h1>
<form onSubmit={handleSubmit} className="max-w-2xl w-full px-6">
<div className="relative flex items-center w-full">
<ModelSelector
Expand All @@ -170,9 +217,9 @@ export function ChatPanel({ messages, query, onModelChange }: ChatPanelProps) {
rows={1}
maxRows={5}
tabIndex={0}
placeholder="我是AI分析引擎,请输入您想查询的问题..."
onCompositionStart={handleCompositionStart}
onCompositionEnd={handleCompositionEnd}
placeholder="Ask a question..."
spellCheck={false}
value={input}
className="resize-none w-full min-h-12 rounded-fill bg-muted border border-input pl-4 pr-10 pt-3 pb-1 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium 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"
Expand Down Expand Up @@ -235,6 +282,7 @@ export function ChatPanel({ messages, query, onModelChange }: ChatPanelProps) {
className={cn(showEmptyScreen ? 'visible' : 'invisible')}
/>
</form>
<MoreTools />
</div>
)
}
Loading