diff --git a/components/auth/user-menu.tsx b/components/auth/user-menu.tsx
index 3998c42..f2b68b1 100644
--- a/components/auth/user-menu.tsx
+++ b/components/auth/user-menu.tsx
@@ -9,6 +9,7 @@ import {
CircleUser,
CreditCard,
Gift,
+ LogIn,
LogOut,
MessageCircleQuestion,
Monitor,
@@ -31,7 +32,7 @@ import {
const FOOTER_LINKS = [
{ href: "/settings/preferences", label: "Profile", Icon: CircleUser },
{ href: "/settings", label: "Account Settings", Icon: SettingsIcon },
- { href: "/settings/billing", label: "Pricing", Icon: CreditCard },
+ { href: "/settings/billing", label: "Pricing", Icon: CreditCard, external: true },
{
href: "https://github.com/OpenSIN-Code/SIN-Code-WebUI-v2#readme",
label: "Documentation",
@@ -58,35 +59,19 @@ export function UserMenu() {
const { data: session, isPending } = useSession()
const { theme, setTheme } = useTheme()
- if (isPending) {
- return (
-
-
- …
-
-
- )
- }
-
- if (!session?.user) {
- return (
-
- )
- }
-
- const user = session.user as { name?: string | null; email?: string | null }
- const displayName = user.name || user.email || "User"
- const initial = (user.name || user.email || "U").charAt(0).toUpperCase()
- const email = user.email || ""
+ // Show trigger even while pending / unauthenticated — the v0-style dropdown
+ // is always present at the sidebar footer.
+ const user = session?.user as
+ | { name?: string | null; email?: string | null }
+ | undefined
+ const isLoggedIn = Boolean(user?.name || user?.email)
+ const displayName = isLoggedIn
+ ? user!.name || user!.email || "User"
+ : "Anmelden"
+ const initial = isLoggedIn
+ ? (user!.name || user!.email || "U").charAt(0).toUpperCase()
+ : "?"
+ const email = user?.email || ""
async function handleSignOut() {
const { signOut } = await import("@/lib/auth/client")
@@ -95,32 +80,50 @@ export function UserMenu() {
router.refresh()
}
+ function handleSignIn() {
+ router.push("/login")
+ }
+
return (
}
>
- {initial}
+ {isPending ? "…" : initial}
{displayName}
-
-
- {displayName}
-
- {email ? (
- {email}
- ) : null}
-
+ {isLoggedIn ? (
+
+
+ {displayName}
+
+ {email ? (
+ {email}
+ ) : null}
+
+ ) : (
+
+
+ Nicht angemeldet
+
+
+ Melde dich an, um Chats zu speichern
+
+
+ )}
@@ -129,7 +132,12 @@ export function UserMenu() {
key={label}
render={
external ? (
-
+
{label}
@@ -194,7 +202,7 @@ export function UserMenu() {
- {/* Chat Position — stub (single layout) */}
+ {/* Chat Position — stub */}
Chat Position
-
-
- Sign Out
-
- }
- />
+ {isLoggedIn ? (
+
+
+ Sign Out
+
+ }
+ />
+ ) : (
+
+
+ Anmelden
+
+ }
+ />
+ )}
diff --git a/components/chat/chat-view.tsx b/components/chat/chat-view.tsx
index f20710c..a7d2076 100644
--- a/components/chat/chat-view.tsx
+++ b/components/chat/chat-view.tsx
@@ -1,12 +1,15 @@
"use client"
-import { useRef, useEffect } from "react"
+import { useRef, useEffect, useState } from "react"
+import { Check, Copy, SquareTerminal } from "lucide-react"
import { Message } from "@/components/chat/message"
import { ThinkingIndicator, LoadingDots } from "@/components/chat/thinking-indicator"
import { ToolCall } from "@/components/chat/tool-call"
import { PromptComposer } from "@/components/chat/prompt-composer"
import { MarkdownMessage } from "@/components/chat/markdown-message"
import { ChatHeader } from "@/components/chat/chat-header"
+import { DashedSpinner, Starburst } from "@/components/icons"
+import { cn } from "@/lib/utils"
export interface ChatPart {
type: "text" | "tool"
@@ -32,6 +35,51 @@ interface ChatViewProps {
title?: string
}
+/* v0-style code block with copy button (preserved from initial commit) */
+function CopyCodeBlock({ body, lang = "code" }: { body: string; lang?: string }) {
+ const [copied, setCopied] = useState(false)
+ async function handleCopy() {
+ try {
+ await navigator.clipboard.writeText(body)
+ setCopied(true)
+ setTimeout(() => setCopied(false), 1500)
+ } catch {
+ /* clipboard not available */
+ }
+ }
+ return (
+
+
+
+
+ {lang}
+
+
+
+
+ {body}
+
+
+ )
+}
+
+/* v0-style tool badge (preserved from initial commit) */
+function ToolBadge({ name }: { name: string }) {
+ return (
+
+
+ {name}
+
+ )
+}
+
export function ChatView({
messages,
status,
@@ -48,19 +96,22 @@ export function ChatView({
const isStreaming = status === "streaming"
const lastMessage = messages[messages.length - 1]
+ const hasMessages = messages.length > 0
return (
-
- {messages.length === 0 && (
-
-
+
+ {!hasMessages && (
+
+
+
What do you want to create?
-
+
Ask the SIN-Code agent to build, fix, or explain anything.
+ Press Enter to send.
)}
@@ -68,6 +119,18 @@ export function ChatView({
{messages.map((msg) => {
const streamingThis =
isStreaming && msg.id === lastMessage?.id && msg.role === "assistant"
+ if (msg.role === "user") {
+ return (
+
+
+ {msg.parts
+ .filter((p) => p.type === "text")
+ .map((p) => p.text)
+ .join("")}
+
+
+ )
+ }
return (