chore: format files

This commit is contained in:
d-kimsuon
2025-08-30 03:18:42 +09:00
parent 16ec283e50
commit bfa19a6e85
54 changed files with 539 additions and 541 deletions

View File

@@ -1,9 +1,8 @@
"use client";
import { ArrowLeftIcon } from "lucide-react";
import { ArrowLeftIcon, FolderIcon, MessageSquareIcon } from "lucide-react";
import Link from "next/link";
import { FolderIcon, MessageSquareIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
@@ -11,11 +10,9 @@ import {
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { useProject } from "../hooks/useProject";
import { pagesPath } from "../../../../lib/$path";
import { parseCommandXml } from "../../../../server/service/parseCommandXml";
import { useProject } from "../hooks/useProject";
import { firstCommandToTitle } from "../services/firstCommandToTitle";
export const ProjectPageContent = ({ projectId }: { projectId: string }) => {
const {
@@ -78,31 +75,9 @@ export const ProjectPageContent = ({ projectId }: { projectId: string }) => {
<CardHeader>
<CardTitle className="flex items-center gap-2">
<span className="break-all overflow-ellipsis line-clamp-2 text-xl">
{session.meta.firstContent
? (() => {
const parsed = parseCommandXml(
session.meta.firstContent
);
if (parsed.kind === "command") {
return (
<span>
{parsed.commandName} {parsed.commandArgs}
</span>
);
}
if (parsed.kind === "local-command-1") {
return (
<span>
{parsed.commandName} {parsed.commandMessage}
</span>
);
}
if (parsed.kind === "local-command-2") {
return <span>{parsed.stdout}</span>;
}
return <span>{session.meta.firstContent}</span>;
})()
: ""}
{session.meta.firstCommand !== null
? firstCommandToTitle(session.meta.firstCommand)
: session.id}
</span>
</CardTitle>
<CardDescription className="font-mono text-xs">
@@ -117,7 +92,7 @@ export const ProjectPageContent = ({ projectId }: { projectId: string }) => {
Last modified:{" "}
{session.meta.lastModifiedAt
? new Date(
session.meta.lastModifiedAt
session.meta.lastModifiedAt,
).toLocaleDateString()
: ""}
</p>
@@ -129,7 +104,7 @@ export const ProjectPageContent = ({ projectId }: { projectId: string }) => {
<Button asChild className="w-full">
<Link
href={`/projects/${projectId}/sessions/${encodeURIComponent(
session.id
session.id,
)}`}
>
View Session

View File

@@ -1,40 +0,0 @@
import { Card, CardContent, CardHeader } from "@/components/ui/card";
import { Skeleton } from "@/components/ui/skeleton";
export default function ProjectLoading() {
return (
<div className="container mx-auto px-4 py-8">
<header className="mb-8">
<Skeleton className="h-9 w-32 mb-4" />
<div className="flex items-center gap-3 mb-2">
<Skeleton className="w-6 h-6" />
<Skeleton className="h-9 w-80" />
</div>
<Skeleton className="h-4 w-96" />
</header>
<main>
<section>
<Skeleton className="h-7 w-64 mb-4" />
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
{Array.from({ length: 6 }).map((_, index) => (
<Card key={index}>
<CardHeader>
<Skeleton className="h-6 w-3/4" />
<Skeleton className="h-4 w-1/2" />
</CardHeader>
<CardContent className="space-y-2">
<Skeleton className="h-4 w-24" />
<Skeleton className="h-4 w-32" />
<Skeleton className="h-3 w-full" />
<Skeleton className="h-10 w-full mt-4" />
</CardContent>
</Card>
))}
</div>
</section>
</main>
</div>
);
}

View File

@@ -0,0 +1,17 @@
import type { ParsedCommand } from "../../../../server/service/parseCommandXml";
export const firstCommandToTitle = (firstCommand: ParsedCommand) => {
switch (firstCommand.kind) {
case "command":
return `${firstCommand.commandName} ${firstCommand.commandArgs}`;
case "local-command-1":
return firstCommand.commandMessage;
case "local-command-2":
return firstCommand.stdout;
case "text":
return firstCommand.content;
default:
firstCommand satisfies never;
throw new Error("Invalid first command");
}
};

View File

@@ -1,7 +1,7 @@
const regExp = /<(?<tag>[^>]+)>(?<content>\s*[^<]*?\s*)<\/\k<tag>>/g;
export const parseCommandXml = (
content: string
content: string,
):
| {
kind: "command";
@@ -37,16 +37,16 @@ export const parseCommandXml = (
}
const commandName = matches.find(
(match) => match.tag === "command-name"
(match) => match.tag === "command-name",
)?.content;
const commandArgs = matches.find(
(match) => match.tag === "command-args"
(match) => match.tag === "command-args",
)?.content;
const commandMessage = matches.find(
(match) => match.tag === "command-message"
(match) => match.tag === "command-message",
)?.content;
const localCommandStdout = matches.find(
(match) => match.tag === "local-command-stdout"
(match) => match.tag === "local-command-stdout",
)?.content;
switch (true) {
@@ -74,4 +74,4 @@ export const parseCommandXml = (
content,
};
}
};
};

View File

@@ -0,0 +1,54 @@
"use client";
import { ArrowLeftIcon, MessageSquareIcon } from "lucide-react";
import Link from "next/link";
import type { FC } from "react";
import { Button } from "@/components/ui/button";
import { firstCommandToTitle } from "../../../services/firstCommandToTitle";
import { useSession } from "../hooks/useSession";
import { ConversationList } from "./conversationList/ConversationList";
export const SessionPageContent: FC<{
projectId: string;
sessionId: string;
}> = ({ projectId, sessionId }) => {
const { session, conversations, getToolResult } = useSession(
projectId,
sessionId,
);
return (
<div className="container mx-auto px-4 py-8">
<header className="mb-8">
<Button asChild variant="ghost" className="mb-4">
<Link
href={`/projects/${projectId}`}
className="flex items-center gap-2"
>
<ArrowLeftIcon className="w-4 h-4" />
Back to Session List
</Link>
</Button>
<div className="flex items-center gap-3 mb-2">
<MessageSquareIcon className="w-6 h-6" />
<h1 className="text-3xl font-bold">
{session.meta.firstCommand !== null
? firstCommandToTitle(session.meta.firstCommand)
: sessionId}
</h1>
</div>
<p className="text-muted-foreground font-mono text-sm">
Session ID: {sessionId}
</p>
</header>
<main>
<ConversationList
conversations={conversations}
getToolResult={getToolResult}
/>
</main>
</div>
);
};

View File

@@ -1,12 +1,6 @@
import { ToolResultContent } from "@/lib/conversation-schema/content/ToolResultContentSchema";
import { AssistantMessageContent } from "@/lib/conversation-schema/message/AssistantMessageSchema";
import { FC } from "react";
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible";
import { ChevronDown, FileText, Lightbulb, Settings } from "lucide-react";
import type { FC } from "react";
import { Badge } from "@/components/ui/badge";
import {
Card,
CardContent,
@@ -14,8 +8,13 @@ import {
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { ChevronDown, Lightbulb, Settings, FileText } from "lucide-react";
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible";
import type { ToolResultContent } from "@/lib/conversation-schema/content/ToolResultContentSchema";
import type { AssistantMessageContent } from "@/lib/conversation-schema/message/AssistantMessageSchema";
import { MarkdownContent } from "../../../../../../components/MarkdownContent";
export const AssistantConversationContent: FC<{

View File

@@ -1,11 +1,11 @@
import type { FC } from "react";
import type { Conversation } from "@/lib/conversation-schema";
import type { ToolResultContent } from "@/lib/conversation-schema/content/ToolResultContentSchema";
import type { FC } from "react";
import { UserConversationContent } from "./UserConversationContent";
import { AssistantConversationContent } from "./AssistantConversationContent";
import { MetaConversationContent } from "./MetaConversationContent";
import { SystemConversationContent } from "./SystemConversationContent";
import { SummaryConversationContent } from "./SummaryConversationContent";
import { SystemConversationContent } from "./SystemConversationContent";
import { UserConversationContent } from "./UserConversationContent";
export const ConversationItem: FC<{
conversation: Conversation;

View File

@@ -1,8 +1,8 @@
"use client";
import type { Conversation } from "@/lib/conversation-schema";
import type { FC } from "react";
import { useConversations } from "../../hooks/useConversations";
import type { Conversation } from "@/lib/conversation-schema";
import type { ToolResultContent } from "@/lib/conversation-schema/content/ToolResultContentSchema";
import { ConversationItem } from "./ConversationItem";
const getConversationKey = (conversation: Conversation) => {
@@ -26,19 +26,14 @@ const getConversationKey = (conversation: Conversation) => {
};
type ConversationListProps = {
projectId: string;
sessionId: string;
conversations: Conversation[];
getToolResult: (toolUseId: string) => ToolResultContent | undefined;
};
export const ConversationList: FC<ConversationListProps> = ({
projectId,
sessionId,
conversations,
getToolResult,
}) => {
const { conversations, getToolResult } = useConversations(
projectId,
sessionId
);
return (
<ul>
{conversations.flatMap((conversation) => {
@@ -60,8 +55,8 @@ export const ConversationList: FC<ConversationListProps> = ({
conversation.type === "user"
? "justify-end"
: conversation.type === "assistant"
? "justify-start"
: "justify-center"
? "justify-start"
: "justify-center"
}`}
key={getConversationKey(conversation)}
>
@@ -70,8 +65,8 @@ export const ConversationList: FC<ConversationListProps> = ({
conversation.type === "user"
? "w-[90%]"
: conversation.type === "assistant"
? "w-[90%]"
: "w-[100%]"
? "w-[90%]"
: "w-[100%]"
}`}
>
{elm}

View File

@@ -1,10 +1,10 @@
import { ChevronDown } from "lucide-react";
import type { FC, PropsWithChildren } from "react";
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible";
import { ChevronDown } from "lucide-react";
export const MetaConversationContent: FC<PropsWithChildren> = ({
children,

View File

@@ -1,10 +1,10 @@
import { ChevronDown } from "lucide-react";
import type { FC, PropsWithChildren } from "react";
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible";
import { ChevronDown } from "lucide-react";
export const SummaryConversationContent: FC<PropsWithChildren> = ({
children,

View File

@@ -1,10 +1,10 @@
import { ChevronDown } from "lucide-react";
import type { FC, PropsWithChildren } from "react";
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible";
import { ChevronDown } from "lucide-react";
export const SystemConversationContent: FC<PropsWithChildren> = ({
children,

View File

@@ -1,9 +1,8 @@
import type { FC } from "react";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Terminal } from "lucide-react";
import type { FC } from "react";
import { parseCommandXml } from "@/app/projects/[projectId]/services/parseCommandXml";
import { Badge } from "@/components/ui/badge";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { MarkdownContent } from "../../../../../../components/MarkdownContent";
export const TextContent: FC<{ text: string }> = ({ text }) => {
@@ -11,7 +10,7 @@ export const TextContent: FC<{ text: string }> = ({ text }) => {
if (parsed.kind === "command") {
return (
<Card className="border-green-200 bg-green-50/50 dark:border-green-800 dark:bg-green-950/20 gap-2 py-3">
<Card className="border-green-200 bg-green-50/50 dark:border-green-800 dark:bg-green-950/20 gap-2 py-3 mb-3">
<CardHeader className="py-0 px-4">
<div className="flex items-center gap-2">
<Terminal className="h-4 w-4 text-green-600 dark:text-green-400" />
@@ -46,7 +45,7 @@ export const TextContent: FC<{ text: string }> = ({ text }) => {
if (parsed.kind === "local-command-1") {
return (
<Card className="border-green-200 bg-green-50/50 dark:border-green-800 dark:bg-green-950/20 gap-2 py-3">
<Card className="border-green-200 bg-green-50/50 dark:border-green-800 dark:bg-green-950/20 gap-2 py-3 mb-3">
<CardHeader className="py-0 px-4">
<div className="flex items-center gap-2">
<Terminal className="h-4 w-4 text-green-600 dark:text-green-400" />
@@ -62,7 +61,7 @@ export const TextContent: FC<{ text: string }> = ({ text }) => {
if (parsed.kind === "local-command-2") {
return (
<Card className="border-green-200 bg-green-50/50 dark:border-green-800 dark:bg-green-950/20 gap-2 py-3">
<Card className="border-green-200 bg-green-50/50 dark:border-green-800 dark:bg-green-950/20 gap-2 py-3 mb-3">
<CardHeader className="py-0 px-4">
<div className="flex items-center gap-2">
<Terminal className="h-4 w-4 text-green-600 dark:text-green-400" />

View File

@@ -1,5 +1,7 @@
import type { UserMessageContent } from "@/lib/conversation-schema/message/UserMessageSchema";
import { AlertCircle, Image as ImageIcon } from "lucide-react";
import Image from "next/image";
import type { FC } from "react";
import { Badge } from "@/components/ui/badge";
import {
Card,
CardContent,
@@ -7,8 +9,7 @@ import {
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Image as ImageIcon, AlertCircle } from "lucide-react";
import type { UserMessageContent } from "@/lib/conversation-schema/message/UserMessageSchema";
import { TextContent } from "./TextContent";
export const UserConversationContent: FC<{
@@ -43,7 +44,7 @@ export const UserConversationContent: FC<{
</CardHeader>
<CardContent>
<div className="rounded-lg border overflow-hidden bg-background">
<img
<Image
src={`data:${content.source.media_type};base64,${content.source.data}`}
alt="User uploaded content"
className="max-w-full h-auto max-h-96 object-contain"

View File

@@ -1,8 +1,8 @@
import { useConversationsQuery } from "./useConversationsQuery";
import { useCallback, useMemo } from "react";
import { useSessionQuery } from "./useSessionQuery";
export const useConversations = (projectId: string, sessionId: string) => {
const query = useConversationsQuery(projectId, sessionId);
export const useSession = (projectId: string, sessionId: string) => {
const query = useSessionQuery(projectId, sessionId);
const toolResultMap = useMemo(() => {
const entries = query.data.session.conversations.flatMap((conversation) => {
@@ -34,10 +34,11 @@ export const useConversations = (projectId: string, sessionId: string) => {
(toolUseId: string) => {
return toolResultMap.get(toolUseId);
},
[toolResultMap]
[toolResultMap],
);
return {
session: query.data.session,
conversations: query.data.session.conversations,
getToolResult,
};

View File

@@ -1,7 +1,7 @@
import { useSuspenseQuery } from "@tanstack/react-query";
import { honoClient } from "../../../../../../lib/api/client";
export const useConversationsQuery = (projectId: string, sessionId: string) => {
export const useSessionQuery = (projectId: string, sessionId: string) => {
return useSuspenseQuery({
queryKey: ["conversations", sessionId],
queryFn: async () => {

View File

@@ -1,34 +0,0 @@
import { Card, CardContent, CardHeader } from "@/components/ui/card";
import { Skeleton } from "@/components/ui/skeleton";
export default function SessionLoading() {
return (
<div className="container mx-auto px-4 py-8">
<header className="mb-8">
<Skeleton className="h-9 w-32 mb-4" />
<div className="flex items-center gap-3 mb-2">
<Skeleton className="w-6 h-6" />
<Skeleton className="h-9 w-64" />
</div>
<Skeleton className="h-4 w-80" />
</header>
<main>
<Card className="max-w-4xl mx-auto">
<CardHeader>
<Skeleton className="h-6 w-48" />
<Skeleton className="h-4 w-64" />
</CardHeader>
<CardContent className="space-y-4">
<Skeleton className="h-32 w-full" />
<Skeleton className="h-24 w-full" />
<Skeleton className="h-16 w-full" />
<div className="flex justify-center pt-4">
<Skeleton className="h-10 w-32" />
</div>
</CardContent>
</Card>
</main>
</div>
);
}

View File

@@ -1,8 +1,5 @@
import Link from "next/link";
import { ArrowLeftIcon, MessageSquareIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import type { Metadata } from "next";
import { ConversationList } from "./components/conversationList/ConversationList";
import { SessionPageContent } from "./components/SessionPageContent";
type PageParams = {
projectId: string;
@@ -28,31 +25,5 @@ interface SessionPageProps {
export default async function SessionPage({ params }: SessionPageProps) {
const { projectId, sessionId } = await params;
return (
<div className="container mx-auto px-4 py-8">
<header className="mb-8">
<Button asChild variant="ghost" className="mb-4">
<Link
href={`/projects/${projectId}`}
className="flex items-center gap-2"
>
<ArrowLeftIcon className="w-4 h-4" />
Back to Session List
</Link>
</Button>
<div className="flex items-center gap-3 mb-2">
<MessageSquareIcon className="w-6 h-6" />
<h1 className="text-3xl font-bold">Conversation Session</h1>
</div>
<p className="text-muted-foreground font-mono text-sm">
Session ID: {sessionId}
</p>
</header>
<main>
<ConversationList projectId={projectId} sessionId={sessionId} />
</main>
</div>
);
return <SessionPageContent projectId={projectId} sessionId={sessionId} />;
}

View File

@@ -1,16 +1,16 @@
"use client";
import type { FC } from "react";
import {
Card,
CardHeader,
CardTitle,
CardDescription,
CardContent,
} from "@/components/ui/card";
import { FolderIcon } from "lucide-react";
import Link from "next/link";
import type { FC } from "react";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { useProjects } from "../hooks/useProjects";
export const ProjectList: FC = () => {