feat: system information view

This commit is contained in:
d-kimsuon
2025-10-20 03:00:13 +09:00
parent 81a5d31f6e
commit 0047b6b2a2
15 changed files with 881 additions and 180 deletions

View File

@@ -1,7 +1,7 @@
"use client";
import { Trans } from "@lingui/react";
import { ChevronDown, Lightbulb, Settings } from "lucide-react";
import { ChevronDown, Lightbulb, Wrench } from "lucide-react";
import Image from "next/image";
import { useTheme } from "next-themes";
import type { FC } from "react";
@@ -10,6 +10,7 @@ import {
oneDark,
oneLight,
} from "react-syntax-highlighter/dist/esm/styles/prism";
import z from "zod";
import { Badge } from "@/components/ui/badge";
import {
Card,
@@ -25,12 +26,28 @@ import {
} from "@/components/ui/collapsible";
import type { ToolResultContent } from "@/lib/conversation-schema/content/ToolResultContentSchema";
import type { AssistantMessageContent } from "@/lib/conversation-schema/message/AssistantMessageSchema";
import { Button } from "../../../../../../../components/ui/button";
import type { SidechainConversation } from "../../../../../../../lib/conversation-schema";
import { MarkdownContent } from "../../../../../../components/MarkdownContent";
import { SidechainConversationModal } from "../conversationModal/SidechainConversationModal";
const taskToolInputSchema = z.object({
prompt: z.string(),
});
export const AssistantConversationContent: FC<{
content: AssistantMessageContent;
getToolResult: (toolUseId: string) => ToolResultContent | undefined;
}> = ({ content, getToolResult }) => {
getSidechainConversationByPrompt: (
prompt: string,
) => SidechainConversation | undefined;
getSidechainConversations: (rootUuid: string) => SidechainConversation[];
}> = ({
content,
getToolResult,
getSidechainConversationByPrompt,
getSidechainConversations,
}) => {
const { resolvedTheme } = useTheme();
const syntaxTheme = resolvedTheme === "dark" ? oneDark : oneLight;
if (content.type === "text") {
@@ -71,11 +88,48 @@ export const AssistantConversationContent: FC<{
if (content.type === "tool_use") {
const toolResult = getToolResult(content.id);
const taskModal = (() => {
const taskInput =
content.name === "Task"
? taskToolInputSchema.safeParse(content.input)
: undefined;
if (taskInput === undefined || taskInput.success === false) {
return undefined;
}
const conversation = getSidechainConversationByPrompt(
taskInput.data.prompt,
);
if (conversation === undefined) {
return undefined;
}
return (
<SidechainConversationModal
conversation={conversation}
sidechainConversations={getSidechainConversations(
conversation.uuid,
).map((original) => ({
...original,
isSidechain: false,
}))}
getToolResult={getToolResult}
trigger={
<Button variant="outline" size="sm">
View Log
</Button>
}
/>
);
})();
return (
<Card className="border-blue-200 bg-blue-50/50 dark:border-blue-800 dark:bg-blue-950/20 gap-2 py-3 mb-2">
<CardHeader className="py-0 px-4">
<div className="flex items-center gap-2">
<Settings className="h-4 w-4 text-blue-600 dark:text-blue-400" />
<Wrench className="h-4 w-4 text-blue-600 dark:text-blue-400" />
<CardTitle className="text-sm font-medium">
<Trans id="assistant.tool_use" message="Tool Use" />
</CardTitle>
@@ -159,6 +213,7 @@ export const AssistantConversationContent: FC<{
</CollapsibleContent>
</Collapsible>
)}
{taskModal}
</CardContent>
</Card>
);

View File

@@ -1,5 +1,8 @@
import type { FC } from "react";
import type { Conversation } from "@/lib/conversation-schema";
import type {
Conversation,
SidechainConversation,
} from "@/lib/conversation-schema";
import type { ToolResultContent } from "@/lib/conversation-schema/content/ToolResultContentSchema";
import { SidechainConversationModal } from "../conversationModal/SidechainConversationModal";
import { AssistantConversationContent } from "./AssistantConversationContent";
@@ -13,11 +16,15 @@ export const ConversationItem: FC<{
conversation: Conversation;
getToolResult: (toolUseId: string) => ToolResultContent | undefined;
isRootSidechain: (conversation: Conversation) => boolean;
getSidechainConversations: (rootUuid: string) => Conversation[];
getSidechainConversationByPrompt: (
prompt: string,
) => SidechainConversation | undefined;
getSidechainConversations: (rootUuid: string) => SidechainConversation[];
}> = ({
conversation,
getToolResult,
isRootSidechain,
getSidechainConversationByPrompt,
getSidechainConversations,
}) => {
if (conversation.type === "summary") {
@@ -54,13 +61,10 @@ export const ConversationItem: FC<{
conversation={conversation}
sidechainConversations={getSidechainConversations(
conversation.uuid,
).map((original) => {
if (original.type === "summary") return original;
return {
...original,
isSidechain: false,
};
})}
).map((original) => ({
...original,
isSidechain: false,
}))}
getToolResult={getToolResult}
/>
);
@@ -99,6 +103,10 @@ export const ConversationItem: FC<{
<AssistantConversationContent
content={content}
getToolResult={getToolResult}
getSidechainConversationByPrompt={
getSidechainConversationByPrompt
}
getSidechainConversations={getSidechainConversations}
/>
</li>
))}

View File

@@ -126,8 +126,11 @@ export const ConversationList: FC<ConversationListProps> = ({
conversations.filter((conversation) => conversation.type !== "x-error"),
[conversations],
);
const { isRootSidechain, getSidechainConversations } =
useSidechain(validConversations);
const {
isRootSidechain,
getSidechainConversations,
getSidechainConversationByPrompt,
} = useSidechain(validConversations);
return (
<ul>
@@ -148,6 +151,7 @@ export const ConversationList: FC<ConversationListProps> = ({
getToolResult={getToolResult}
isRootSidechain={isRootSidechain}
getSidechainConversations={getSidechainConversations}
getSidechainConversationByPrompt={getSidechainConversationByPrompt}
/>
);

View File

@@ -16,11 +16,13 @@ import type {
SidechainConversation,
} from "@/lib/conversation-schema";
import type { ToolResultContent } from "@/lib/conversation-schema/content/ToolResultContentSchema";
import { extractFirstUserText } from "../../../../../../../server/core/session/functions/extractFirstUserText";
import { ConversationList } from "../conversationList/ConversationList";
type SidechainConversationModalProps = {
conversation: SidechainConversation;
sidechainConversations: Conversation[];
trigger?: React.ReactNode;
getToolResult: (toolUseId: string) => ToolResultContent | undefined;
};
@@ -38,29 +40,12 @@ const sidechainTitle = (conversations: Conversation[]): string => {
return defaultTitle;
}
if (firstConversation.type !== "user") {
return defaultTitle;
}
const textContent =
typeof firstConversation.message.content === "string"
? firstConversation.message.content
: (() => {
const firstContent = firstConversation.message.content.at(0);
if (firstContent === undefined) return null;
if (typeof firstContent === "string") return firstContent;
if (firstContent.type === "text") return firstContent.text;
return null;
})();
return textContent ?? defaultTitle;
return extractFirstUserText(firstConversation) ?? defaultTitle;
};
export const SidechainConversationModal: FC<
SidechainConversationModalProps
> = ({ conversation, sidechainConversations, getToolResult }) => {
> = ({ conversation, sidechainConversations, trigger, getToolResult }) => {
const title = sidechainTitle(sidechainConversations);
const rootUuid = conversation.uuid;
@@ -68,19 +53,21 @@ export const SidechainConversationModal: FC<
return (
<Dialog>
<DialogTrigger asChild>
<Button
variant="outline"
size="sm"
className="w-full mb-3 items-center justify-start"
data-testid="sidechain-task-button"
>
<div className="flex items-center gap-2 overflow-hidden">
<Eye className="h-4 w-4 flex-shrink-0" />
<span className="overflow-hidden text-ellipsis">
View Task: {title}
</span>
</div>
</Button>
{trigger ?? (
<Button
variant="outline"
size="sm"
className="w-full mb-3 items-center justify-start"
data-testid="sidechain-task-button"
>
<div className="flex items-center gap-2 overflow-hidden">
<Eye className="h-4 w-4 flex-shrink-0" />
<span className="overflow-hidden text-ellipsis">
View Task: {title}
</span>
</div>
</Button>
)}
</DialogTrigger>
<DialogContent
className="w-[95vw] md:w-[90vw] max-h-[80vh] overflow-hidden flex flex-col px-2 md:px-8"

View File

@@ -15,6 +15,19 @@ export const useSidechain = (conversations: Conversation[]) => {
);
}, [sidechainConversations]);
const conversationPromptMap = useMemo(() => {
return new Map<string, SidechainConversation>(
sidechainConversations
.filter((conv) => conv.type === "user")
.filter(
(conv) =>
conv.parentUuid === null &&
typeof conv.message.content === "string",
)
.map((conv) => [conv.message.content as string, conv] as const),
);
}, [sidechainConversations]);
const getRootConversationRecursive = useCallback(
(conversation: SidechainConversation): SidechainConversation => {
if (conversation.parentUuid === null) {
@@ -72,8 +85,16 @@ export const useSidechain = (conversations: Conversation[]) => {
[sidechainConversationGroups],
);
const getSidechainConversationByPrompt = useCallback(
(prompt: string) => {
return conversationPromptMap.get(prompt);
},
[conversationPromptMap],
);
return {
isRootSidechain,
getSidechainConversations,
getSidechainConversationByPrompt,
};
};

View File

@@ -2,7 +2,7 @@
import { Trans } from "@lingui/react";
import type { LucideIcon } from "lucide-react";
import { SettingsIcon } from "lucide-react";
import { InfoIcon, SettingsIcon } from "lucide-react";
import { type FC, type ReactNode, Suspense, useState } from "react";
import {
Tooltip,
@@ -13,6 +13,7 @@ import {
import { cn } from "@/lib/utils";
import { NotificationSettings } from "./NotificationSettings";
import { SettingsControls } from "./SettingsControls";
import { SystemInfoCard } from "./SystemInfoCard";
export interface SidebarTab {
id: string;
@@ -94,7 +95,31 @@ export const GlobalSidebar: FC<GlobalSidebarProps> = ({
),
};
const allTabs = [...additionalTabs, settingsTab];
const systemInfoTab: SidebarTab = {
id: "system-info",
icon: InfoIcon,
title: (
<Trans id="settings.section.system_info" message="System Information" />
),
content: (
<Suspense
fallback={
<div className="h-full flex items-center justify-center p-4">
<div className="text-sm text-sidebar-foreground/70">
<Trans
id="system_info.loading"
message="Loading system information..."
/>
</div>
</div>
}
>
<SystemInfoCard />
</Suspense>
),
};
const allTabs = [...additionalTabs, settingsTab, systemInfoTab];
const [activeTab, setActiveTab] = useState<string>(
defaultActiveTab ?? allTabs[allTabs.length - 1]?.id ?? "settings",
);

View File

@@ -0,0 +1,229 @@
"use client";
import { Trans } from "@lingui/react";
import { useSuspenseQuery } from "@tanstack/react-query";
import { CheckCircle2, ChevronDown, ChevronRight, XCircle } from "lucide-react";
import { type FC, type ReactNode, useState } from "react";
import {
claudeCodeFeaturesQuery,
claudeCodeMetaQuery,
systemVersionQuery,
} from "@/lib/api/queries";
import { Badge } from "./ui/badge";
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "./ui/collapsible";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "./ui/tooltip";
interface FeatureInfo {
title: ReactNode;
description: ReactNode;
}
const getFeatureInfo = (featureName: string): FeatureInfo => {
switch (featureName) {
case "canUseTool":
return {
title: (
<Trans
id="system_info.feature.can_use_tool.title"
message="Tool Use Permission Control"
/>
),
description: (
<Trans
id="system_info.feature.can_use_tool.description"
message="Dynamically control tool usage permissions and request user approval before tool execution (v1.0.82+)"
/>
),
};
case "uuidOnSDKMessage":
return {
title: (
<Trans
id="system_info.feature.uuid_on_sdk_message.title"
message="Message UUID Support"
/>
),
description: (
<Trans
id="system_info.feature.uuid_on_sdk_message.description"
message="Adds unique identifiers to SDK messages for better tracking (v1.0.86+)"
/>
),
};
case "agentSdk":
return {
title: (
<Trans
id="system_info.feature.agent_sdk.title"
message="Claude Agent SDK"
/>
),
description: (
<Trans
id="system_info.feature.agent_sdk.description"
message="Uses Claude Agent SDK instead of Claude Code SDK (v1.0.125+)"
/>
),
};
default:
return {
title: featureName,
description: (
<Trans
id="system_info.feature.unknown.description"
message="Feature information not available"
/>
),
};
}
};
export const SystemInfoCard: FC = () => {
const [isExpanded, setIsExpanded] = useState(false);
const { data: versionData } = useSuspenseQuery({
...systemVersionQuery,
});
const { data: claudeCodeMetaData } = useSuspenseQuery({
...claudeCodeMetaQuery,
});
const { data: claudeCodeFeaturesData } = useSuspenseQuery({
...claudeCodeFeaturesQuery,
});
return (
<div className="h-full flex flex-col">
<div className="border-b border-sidebar-border p-4">
<h2 className="font-semibold text-lg">
<Trans id="system_info.title" message="System Information" />
</h2>
<p className="text-xs text-sidebar-foreground/70">
<Trans
id="system_info.description"
message="Version and feature information"
/>
</p>
</div>
<div className="flex-1 overflow-y-auto p-4 space-y-6">
{/* Claude Code Viewer Version */}
<div className="space-y-3">
<h3 className="font-medium text-sm text-sidebar-foreground">
<Trans
id="system_info.viewer_version"
message="Claude Code Viewer"
/>
</h3>
<div className="flex justify-between items-center pl-2">
<span className="text-xs text-sidebar-foreground/70">
<Trans id="system_info.version_label" message="Version" />
</span>
<Badge variant="secondary" className="text-xs font-mono">
v{versionData?.version || "Unknown"}
</Badge>
</div>
</div>
{/* Claude Code Information */}
<div className="space-y-3">
<h3 className="font-medium text-sm text-sidebar-foreground">
<Trans id="system_info.claude_code" message="Claude Code" />
</h3>
<div className="space-y-2 pl-2">
<div className="space-y-1">
<div className="text-xs text-sidebar-foreground/70">
<Trans id="system_info.executable_path" message="Executable" />
</div>
<div className="text-xs text-sidebar-foreground font-mono break-all">
{claudeCodeMetaData?.executablePath || (
<span className="text-sidebar-foreground/50">
<Trans id="system_info.unknown" message="Unknown" />
</span>
)}
</div>
</div>
<div className="flex justify-between items-center pt-1">
<span className="text-xs text-sidebar-foreground/70">
<Trans id="system_info.version_label" message="Version" />
</span>
<Badge variant="secondary" className="text-xs font-mono">
{claudeCodeMetaData?.version || (
<Trans id="system_info.unknown" message="Unknown" />
)}
</Badge>
</div>
</div>
</div>
{/* Available Features */}
<div className="space-y-3">
<Collapsible open={isExpanded} onOpenChange={setIsExpanded}>
<CollapsibleTrigger className="flex w-full items-center justify-between group">
<h3 className="font-medium text-sm text-sidebar-foreground">
<Trans
id="system_info.available_features"
message="Available Features"
/>
</h3>
{isExpanded ? (
<ChevronDown className="h-4 w-4 text-sidebar-foreground/70 group-hover:text-sidebar-foreground transition-colors" />
) : (
<ChevronRight className="h-4 w-4 text-sidebar-foreground/70 group-hover:text-sidebar-foreground transition-colors" />
)}
</CollapsibleTrigger>
<CollapsibleContent className="pt-3">
<TooltipProvider>
<ul className="space-y-2 pl-2">
{claudeCodeFeaturesData?.features.map(({ name, enabled }) => {
const featureInfo = getFeatureInfo(name);
return (
<li key={name} className="flex items-start gap-2">
{enabled ? (
<CheckCircle2 className="h-3.5 w-3.5 text-green-500 dark:text-green-400 mt-0.5 flex-shrink-0" />
) : (
<XCircle className="h-3.5 w-3.5 text-sidebar-foreground/30 mt-0.5 flex-shrink-0" />
)}
<Tooltip>
<TooltipTrigger asChild>
<span
className={
enabled
? "text-xs text-sidebar-foreground cursor-help"
: "text-xs text-sidebar-foreground/50 line-through cursor-help"
}
>
{featureInfo.title}
</span>
</TooltipTrigger>
<TooltipContent
side="right"
className="max-w-xs text-xs"
>
{featureInfo.description}
</TooltipContent>
</Tooltip>
</li>
);
})}
</ul>
</TooltipProvider>
</CollapsibleContent>
</Collapsible>
</div>
</div>
</div>
);
};

View File

@@ -206,3 +206,46 @@ export const configQuery = {
return await response.json();
},
} as const;
export const systemVersionQuery = {
queryKey: ["version"],
queryFn: async () => {
const response = await honoClient.api.version.$get();
if (!response.ok) {
throw new Error(`Failed to fetch system version: ${response.statusText}`);
}
return await response.json();
},
} as const;
export const claudeCodeMetaQuery = {
queryKey: ["cc", "meta"],
queryFn: async () => {
const response = await honoClient.api.cc.meta.$get();
if (!response.ok) {
throw new Error(
`Failed to fetch system features: ${response.statusText}`,
);
}
return await response.json();
},
} as const;
export const claudeCodeFeaturesQuery = {
queryKey: ["cc", "features"],
queryFn: async () => {
const response = await honoClient.api.cc.features.$get();
if (!response.ok) {
throw new Error(
`Failed to fetch claude code features: ${response.statusText}`,
);
}
return await response.json();
},
} as const;

View File

@@ -23,28 +23,6 @@
"origin": [["src/components/SettingsControls.tsx", 306]],
"translation": "Select theme"
},
"Reload MCP servers": {
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/sessionSidebar/McpTab.tsx",
42
]
],
"translation": "Reload MCP servers"
},
"Close sidebar": {
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/sessionSidebar/MobileSidebar.tsx",
173
]
],
"translation": "Close sidebar"
},
"Type your message... (Start with / for commands, @ for files, Enter to send)": {
"placeholders": {},
"comments": [],
@@ -90,6 +68,28 @@
],
"translation": "Type your message... (Start with / for commands, @ for files, Shift+Enter to send)"
},
"Reload MCP servers": {
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/sessionSidebar/McpTab.tsx",
42
]
],
"translation": "Reload MCP servers"
},
"Close sidebar": {
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/sessionSidebar/MobileSidebar.tsx",
173
]
],
"translation": "Close sidebar"
},
"Type your message here... (Start with / for commands, @ for files, Enter to send)": {
"placeholders": {},
"comments": [],
@@ -114,36 +114,6 @@
],
"translation": "Type your message here... (Start with / for commands, @ for files, Shift+Enter to send)"
},
"Available commands": {
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/components/chatForm/CommandCompletion.tsx",
193
]
],
"translation": "Available commands"
},
"Message input with completion support": {
"placeholders": {},
"comments": [],
"origin": [
["src/app/projects/[projectId]/components/chatForm/ChatInput.tsx", 210]
],
"translation": "Message input with completion support"
},
"Available files and directories": {
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/components/chatForm/FileCompletion.tsx",
267
]
],
"translation": "Available files and directories"
},
"Uncommitted changes": {
"placeholders": {},
"comments": [],
@@ -221,6 +191,36 @@
],
"translation": "Compare to"
},
"Available commands": {
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/components/chatForm/CommandCompletion.tsx",
193
]
],
"translation": "Available commands"
},
"Available files and directories": {
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/components/chatForm/FileCompletion.tsx",
267
]
],
"translation": "Available files and directories"
},
"Message input with completion support": {
"placeholders": {},
"comments": [],
"origin": [
["src/app/projects/[projectId]/components/chatForm/ChatInput.tsx", 210]
],
"translation": "Message input with completion support"
},
"session.conversation.abort": {
"message": "Abort",
"placeholders": {},
@@ -244,6 +244,13 @@
"origin": [["src/components/SettingsControls.tsx", 234]],
"translation": "Accept Edits (Auto-approve file edits)"
},
"system_info.feature.uuid_on_sdk_message.description": {
"message": "Adds unique identifiers to SDK messages for better tracking (v1.0.86+)",
"placeholders": {},
"comments": [],
"origin": [["src/components/SystemInfoCard.tsx", 56]],
"translation": "Adds unique identifiers to SDK messages for better tracking (v1.0.86+)"
},
"chat.autocomplete.active": {
"message": "Autocomplete active",
"placeholders": {},
@@ -253,6 +260,13 @@
],
"translation": "Autocomplete active"
},
"system_info.available_features": {
"message": "Available Features",
"placeholders": {},
"comments": [],
"origin": [["src/components/SystemInfoCard.tsx", 175]],
"translation": "Available Features"
},
"sidebar.back.to.projects": {
"message": "Back to projects",
"placeholders": {},
@@ -273,7 +287,7 @@
"message": "Back to Projects",
"placeholders": {},
"comments": [],
"origin": [["src/app/projects/[projectId]/not-found.tsx", 42]],
"origin": [["src/app/projects/[projectId]/not-found.tsx", 45]],
"translation": "Back to Projects"
},
"project.error.back_to_projects": {
@@ -339,6 +353,20 @@
"origin": [["src/components/SettingsControls.tsx", 290]],
"translation": "Choose your preferred language"
},
"system_info.feature.agent_sdk.title": {
"message": "Claude Agent SDK",
"placeholders": {},
"comments": [],
"origin": [["src/components/SystemInfoCard.tsx", 65]],
"translation": "Claude Agent SDK"
},
"system_info.claude_code": {
"message": "Claude Code",
"placeholders": {},
"comments": [],
"origin": [["src/components/SystemInfoCard.tsx", 141]],
"translation": "Claude Code"
},
"session.processing": {
"message": "Claude Code is processing...",
"placeholders": {},
@@ -351,6 +379,13 @@
],
"translation": "Claude Code is processing..."
},
"system_info.viewer_version": {
"message": "Claude Code Viewer",
"placeholders": {},
"comments": [],
"origin": [["src/components/SystemInfoCard.tsx", 123]],
"translation": "Claude Code Viewer"
},
"settings.input.enter_key_behavior.command_enter": {
"message": "Command+Enter to send",
"placeholders": {},
@@ -519,9 +554,16 @@
"message": "Display and behavior preferences",
"placeholders": {},
"comments": [],
"origin": [["src/components/GlobalSidebar.tsx", 55]],
"origin": [["src/components/GlobalSidebar.tsx", 56]],
"translation": "Display and behavior preferences"
},
"system_info.feature.can_use_tool.description": {
"message": "Dynamically control tool usage permissions and request user approval before tool execution (v1.0.82+)",
"placeholders": {},
"comments": [],
"origin": [["src/components/SystemInfoCard.tsx", 41]],
"translation": "Dynamically control tool usage permissions and request user approval before tool execution (v1.0.82+)"
},
"settings.locale.en": {
"message": "English",
"placeholders": {},
@@ -581,6 +623,13 @@
"origin": [["src/app/projects/[projectId]/error.tsx", 58]],
"translation": "Error ID:"
},
"system_info.executable_path": {
"message": "Executable",
"placeholders": {},
"comments": [],
"origin": [["src/components/SystemInfoCard.tsx", 146]],
"translation": "Executable"
},
"mcp.error.load_failed": {
"message": "Failed to load MCP servers: {error}",
"placeholders": {
@@ -611,6 +660,13 @@
],
"translation": "Failed to send message. Please try again."
},
"system_info.feature.unknown.description": {
"message": "Feature information not available",
"placeholders": {},
"comments": [],
"origin": [["src/components/SystemInfoCard.tsx", 81]],
"translation": "Feature information not available"
},
"diff.files": {
"message": "files",
"placeholders": {},
@@ -661,7 +717,7 @@
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/conversationList/AssistantConversationContent.tsx",
98
152
]
],
"translation": "Input Parameters"
@@ -727,10 +783,17 @@
"src/app/projects/[projectId]/sessions/[sessionId]/components/sessionSidebar/MobileSidebar.tsx",
115
],
["src/components/GlobalSidebar.tsx", 66]
["src/components/GlobalSidebar.tsx", 67]
],
"translation": "Loading settings..."
},
"system_info.loading": {
"message": "Loading system information...",
"placeholders": {},
"comments": [],
"origin": [["src/components/GlobalSidebar.tsx", 109]],
"translation": "Loading system information..."
},
"directory_picker.loading": {
"message": "Loading...",
"placeholders": {},
@@ -782,6 +845,13 @@
],
"translation": "Media type not supported for display"
},
"system_info.feature.uuid_on_sdk_message.title": {
"message": "Message UUID Support",
"placeholders": {},
"comments": [],
"origin": [["src/components/SystemInfoCard.tsx", 50]],
"translation": "Message UUID Support"
},
"project_list.messages": {
"message": "Messages:",
"placeholders": {},
@@ -852,7 +922,7 @@
"message": "Notifications",
"placeholders": {},
"comments": [],
"origin": [["src/components/GlobalSidebar.tsx", 84]],
"origin": [["src/components/GlobalSidebar.tsx", 85]],
"translation": "Notifications"
},
"settings.notifications": {
@@ -927,7 +997,7 @@
"message": "Project Not Found",
"placeholders": {},
"comments": [],
"origin": [["src/app/projects/[projectId]/not-found.tsx", 23]],
"origin": [["src/app/projects/[projectId]/not-found.tsx", 26]],
"translation": "Project Not Found"
},
"diff.push": {
@@ -1094,7 +1164,7 @@
"message": "Session Display",
"placeholders": {},
"comments": [],
"origin": [["src/components/GlobalSidebar.tsx", 74]],
"origin": [["src/components/GlobalSidebar.tsx", 75]],
"translation": "Session Display"
},
"settings.session.display": {
@@ -1125,7 +1195,7 @@
"message": "Settings",
"placeholders": {},
"comments": [],
"origin": [["src/components/GlobalSidebar.tsx", 52]],
"origin": [["src/components/GlobalSidebar.tsx", 53]],
"translation": "Settings"
},
"settings.tab.title": {
@@ -1137,7 +1207,7 @@
"src/app/projects/[projectId]/sessions/[sessionId]/components/sessionSidebar/MobileSidebar.tsx",
278
],
["src/components/GlobalSidebar.tsx", 43]
["src/components/GlobalSidebar.tsx", 44]
],
"translation": "Settings for display and notifications"
},
@@ -1204,6 +1274,20 @@
"origin": [["src/components/SettingsControls.tsx", 316]],
"translation": "System"
},
"system_info.title": {
"message": "System Information",
"placeholders": {},
"comments": [],
"origin": [["src/components/SystemInfoCard.tsx", 109]],
"translation": "System Information"
},
"settings.section.system_info": {
"message": "System Information",
"placeholders": {},
"comments": [],
"origin": [["src/components/GlobalSidebar.tsx", 102]],
"translation": "System Information"
},
"notification.test": {
"message": "Test",
"placeholders": {},
@@ -1215,7 +1299,7 @@
"message": "The project you are looking for does not exist or has been removed",
"placeholders": {},
"comments": [],
"origin": [["src/app/projects/[projectId]/not-found.tsx", 29]],
"origin": [["src/app/projects/[projectId]/not-found.tsx", 32]],
"translation": "The project you are looking for does not exist or has been removed"
},
"settings.theme": {
@@ -1232,7 +1316,7 @@
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/conversationList/AssistantConversationContent.tsx",
53
70
]
],
"translation": "Thinking"
@@ -1256,7 +1340,7 @@
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/conversationList/AssistantConversationContent.tsx",
122
176
]
],
"translation": "Tool Result"
@@ -1268,11 +1352,18 @@
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/conversationList/AssistantConversationContent.tsx",
80
134
]
],
"translation": "Tool Use"
},
"system_info.feature.can_use_tool.title": {
"message": "Tool Use Permission Control",
"placeholders": {},
"comments": [],
"origin": [["src/components/SystemInfoCard.tsx", 35]],
"translation": "Tool Use Permission Control"
},
"sessions.total": {
"message": "total",
"placeholders": {},
@@ -1299,6 +1390,16 @@
"origin": [["src/components/SettingsControls.tsx", 144]],
"translation": "Unify sessions with same title"
},
"system_info.unknown": {
"message": "Unknown",
"placeholders": {},
"comments": [],
"origin": [
["src/components/SystemInfoCard.tsx", 151],
["src/components/SystemInfoCard.tsx", 163]
],
"translation": "Unknown"
},
"user.content.unsupported_media": {
"message": "Unsupported Media",
"placeholders": {},
@@ -1323,6 +1424,30 @@
],
"translation": "User uploaded image content"
},
"system_info.feature.agent_sdk.description": {
"message": "Uses Claude Agent SDK instead of Claude Code SDK (v1.0.125+)",
"placeholders": {},
"comments": [],
"origin": [["src/components/SystemInfoCard.tsx", 71]],
"translation": "Uses Claude Agent SDK instead of Claude Code SDK (v1.0.125+)"
},
"system_info.version_label": {
"message": "Version",
"placeholders": {},
"comments": [],
"origin": [
["src/components/SystemInfoCard.tsx", 130],
["src/components/SystemInfoCard.tsx", 159]
],
"translation": "Version"
},
"system_info.description": {
"message": "Version and feature information",
"placeholders": {},
"comments": [],
"origin": [["src/components/SystemInfoCard.tsx", 112]],
"translation": "Version and feature information"
},
"project_list.view_conversations": {
"message": "View Conversations",
"placeholders": {},

File diff suppressed because one or more lines are too long

View File

@@ -23,28 +23,6 @@
"origin": [["src/components/SettingsControls.tsx", 306]],
"translation": "テーマを選択"
},
"Reload MCP servers": {
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/sessionSidebar/McpTab.tsx",
42
]
],
"translation": "MCPサーバーを再読み込み"
},
"Close sidebar": {
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/sessionSidebar/MobileSidebar.tsx",
173
]
],
"translation": "サイドバーを閉じる"
},
"Type your message... (Start with / for commands, @ for files, Enter to send)": {
"placeholders": {},
"comments": [],
@@ -90,6 +68,28 @@
],
"translation": "メッセージを入力... /でコマンド、@でファイル、Shift+Enterで送信"
},
"Reload MCP servers": {
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/sessionSidebar/McpTab.tsx",
42
]
],
"translation": "MCPサーバーを再読み込み"
},
"Close sidebar": {
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/sessionSidebar/MobileSidebar.tsx",
173
]
],
"translation": "サイドバーを閉じる"
},
"Type your message here... (Start with / for commands, @ for files, Enter to send)": {
"placeholders": {},
"comments": [],
@@ -114,36 +114,6 @@
],
"translation": "ここにメッセージを入力... /でコマンド、@でファイル、Shift+Enterで送信"
},
"Available commands": {
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/components/chatForm/CommandCompletion.tsx",
193
]
],
"translation": "利用可能なコマンド"
},
"Message input with completion support": {
"placeholders": {},
"comments": [],
"origin": [
["src/app/projects/[projectId]/components/chatForm/ChatInput.tsx", 210]
],
"translation": "補完機能付きメッセージ入力"
},
"Available files and directories": {
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/components/chatForm/FileCompletion.tsx",
267
]
],
"translation": "利用可能なファイルとディレクトリ"
},
"Uncommitted changes": {
"placeholders": {},
"comments": [],
@@ -221,6 +191,36 @@
],
"translation": "比較先"
},
"Available commands": {
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/components/chatForm/CommandCompletion.tsx",
193
]
],
"translation": "利用可能なコマンド"
},
"Available files and directories": {
"placeholders": {},
"comments": [],
"origin": [
[
"src/app/projects/[projectId]/components/chatForm/FileCompletion.tsx",
267
]
],
"translation": "利用可能なファイルとディレクトリ"
},
"Message input with completion support": {
"placeholders": {},
"comments": [],
"origin": [
["src/app/projects/[projectId]/components/chatForm/ChatInput.tsx", 210]
],
"translation": "補完機能付きメッセージ入力"
},
"session.conversation.abort": {
"message": "Abort",
"placeholders": {},
@@ -244,6 +244,13 @@
"origin": [["src/components/SettingsControls.tsx", 234]],
"translation": "編集を承認(ファイル編集を自動承認)"
},
"system_info.feature.uuid_on_sdk_message.description": {
"message": "Adds unique identifiers to SDK messages for better tracking (v1.0.86+)",
"placeholders": {},
"comments": [],
"origin": [["src/components/SystemInfoCard.tsx", 56]],
"translation": "SDKメッセージに一意の識別子を追加して追跡を改善します (v1.0.86+)"
},
"chat.autocomplete.active": {
"message": "Autocomplete active",
"placeholders": {},
@@ -253,6 +260,13 @@
],
"translation": "オートコンプリート有効"
},
"system_info.available_features": {
"message": "Available Features",
"placeholders": {},
"comments": [],
"origin": [["src/components/SystemInfoCard.tsx", 175]],
"translation": "利用可能機能"
},
"sidebar.back.to.projects": {
"message": "Back to projects",
"placeholders": {},
@@ -273,7 +287,7 @@
"message": "Back to Projects",
"placeholders": {},
"comments": [],
"origin": [["src/app/projects/[projectId]/not-found.tsx", 42]],
"origin": [["src/app/projects/[projectId]/not-found.tsx", 45]],
"translation": "プロジェクト一覧に戻る"
},
"project.error.back_to_projects": {
@@ -339,6 +353,20 @@
"origin": [["src/components/SettingsControls.tsx", 290]],
"translation": "お好みの言語を選択"
},
"system_info.feature.agent_sdk.title": {
"message": "Claude Agent SDK",
"placeholders": {},
"comments": [],
"origin": [["src/components/SystemInfoCard.tsx", 65]],
"translation": "Claude Agent SDK"
},
"system_info.claude_code": {
"message": "Claude Code",
"placeholders": {},
"comments": [],
"origin": [["src/components/SystemInfoCard.tsx", 141]],
"translation": "Claude Code"
},
"session.processing": {
"message": "Claude Code is processing...",
"placeholders": {},
@@ -351,6 +379,13 @@
],
"translation": "Claude Codeが処理中..."
},
"system_info.viewer_version": {
"message": "Claude Code Viewer",
"placeholders": {},
"comments": [],
"origin": [["src/components/SystemInfoCard.tsx", 123]],
"translation": "Claude Code Viewer"
},
"settings.input.enter_key_behavior.command_enter": {
"message": "Command+Enter to send",
"placeholders": {},
@@ -519,9 +554,16 @@
"message": "Display and behavior preferences",
"placeholders": {},
"comments": [],
"origin": [["src/components/GlobalSidebar.tsx", 55]],
"origin": [["src/components/GlobalSidebar.tsx", 56]],
"translation": "表示と動作の設定"
},
"system_info.feature.can_use_tool.description": {
"message": "Dynamically control tool usage permissions and request user approval before tool execution (v1.0.82+)",
"placeholders": {},
"comments": [],
"origin": [["src/components/SystemInfoCard.tsx", 41]],
"translation": "動的にツールの使用許可を制御し、ツール実行前にユーザーの承認を求めることができます (v1.0.82+)"
},
"settings.locale.en": {
"message": "English",
"placeholders": {},
@@ -581,6 +623,13 @@
"origin": [["src/app/projects/[projectId]/error.tsx", 58]],
"translation": "エラーID"
},
"system_info.executable_path": {
"message": "Executable",
"placeholders": {},
"comments": [],
"origin": [["src/components/SystemInfoCard.tsx", 146]],
"translation": "実行ファイル"
},
"mcp.error.load_failed": {
"message": "Failed to load MCP servers: {error}",
"placeholders": {
@@ -611,6 +660,13 @@
],
"translation": "メッセージの送信に失敗しました。もう一度お試しください。"
},
"system_info.feature.unknown.description": {
"message": "Feature information not available",
"placeholders": {},
"comments": [],
"origin": [["src/components/SystemInfoCard.tsx", 81]],
"translation": "機能情報は利用できません"
},
"diff.files": {
"message": "files",
"placeholders": {},
@@ -661,7 +717,7 @@
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/conversationList/AssistantConversationContent.tsx",
98
152
]
],
"translation": "入力パラメータ"
@@ -727,10 +783,17 @@
"src/app/projects/[projectId]/sessions/[sessionId]/components/sessionSidebar/MobileSidebar.tsx",
115
],
["src/components/GlobalSidebar.tsx", 66]
["src/components/GlobalSidebar.tsx", 67]
],
"translation": "設定を読み込み中..."
},
"system_info.loading": {
"message": "Loading system information...",
"placeholders": {},
"comments": [],
"origin": [["src/components/GlobalSidebar.tsx", 109]],
"translation": "システム情報を読み込んでいます..."
},
"directory_picker.loading": {
"message": "Loading...",
"placeholders": {},
@@ -782,6 +845,13 @@
],
"translation": "表示がサポートされていないメディア形式です"
},
"system_info.feature.uuid_on_sdk_message.title": {
"message": "Message UUID Support",
"placeholders": {},
"comments": [],
"origin": [["src/components/SystemInfoCard.tsx", 50]],
"translation": "メッセージUUIDサポート"
},
"project_list.messages": {
"message": "Messages:",
"placeholders": {},
@@ -852,7 +922,7 @@
"message": "Notifications",
"placeholders": {},
"comments": [],
"origin": [["src/components/GlobalSidebar.tsx", 84]],
"origin": [["src/components/GlobalSidebar.tsx", 85]],
"translation": "通知"
},
"settings.notifications": {
@@ -927,7 +997,7 @@
"message": "Project Not Found",
"placeholders": {},
"comments": [],
"origin": [["src/app/projects/[projectId]/not-found.tsx", 23]],
"origin": [["src/app/projects/[projectId]/not-found.tsx", 26]],
"translation": "プロジェクトが見つかりません"
},
"diff.push": {
@@ -1094,7 +1164,7 @@
"message": "Session Display",
"placeholders": {},
"comments": [],
"origin": [["src/components/GlobalSidebar.tsx", 74]],
"origin": [["src/components/GlobalSidebar.tsx", 75]],
"translation": "セッション表示"
},
"settings.session.display": {
@@ -1125,7 +1195,7 @@
"message": "Settings",
"placeholders": {},
"comments": [],
"origin": [["src/components/GlobalSidebar.tsx", 52]],
"origin": [["src/components/GlobalSidebar.tsx", 53]],
"translation": "設定"
},
"settings.tab.title": {
@@ -1137,7 +1207,7 @@
"src/app/projects/[projectId]/sessions/[sessionId]/components/sessionSidebar/MobileSidebar.tsx",
278
],
["src/components/GlobalSidebar.tsx", 43]
["src/components/GlobalSidebar.tsx", 44]
],
"translation": "表示と通知の設定"
},
@@ -1204,6 +1274,20 @@
"origin": [["src/components/SettingsControls.tsx", 316]],
"translation": "システム"
},
"system_info.title": {
"message": "System Information",
"placeholders": {},
"comments": [],
"origin": [["src/components/SystemInfoCard.tsx", 109]],
"translation": "システム情報"
},
"settings.section.system_info": {
"message": "System Information",
"placeholders": {},
"comments": [],
"origin": [["src/components/GlobalSidebar.tsx", 102]],
"translation": "システム情報"
},
"notification.test": {
"message": "Test",
"placeholders": {},
@@ -1215,7 +1299,7 @@
"message": "The project you are looking for does not exist or has been removed",
"placeholders": {},
"comments": [],
"origin": [["src/app/projects/[projectId]/not-found.tsx", 29]],
"origin": [["src/app/projects/[projectId]/not-found.tsx", 32]],
"translation": "お探しのプロジェクトは存在しないか、削除されています"
},
"settings.theme": {
@@ -1232,7 +1316,7 @@
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/conversationList/AssistantConversationContent.tsx",
53
70
]
],
"translation": "思考中"
@@ -1256,7 +1340,7 @@
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/conversationList/AssistantConversationContent.tsx",
122
176
]
],
"translation": "ツール実行結果"
@@ -1268,11 +1352,18 @@
"origin": [
[
"src/app/projects/[projectId]/sessions/[sessionId]/components/conversationList/AssistantConversationContent.tsx",
80
134
]
],
"translation": "ツール使用"
},
"system_info.feature.can_use_tool.title": {
"message": "Tool Use Permission Control",
"placeholders": {},
"comments": [],
"origin": [["src/components/SystemInfoCard.tsx", 35]],
"translation": "ツール使用権限制御"
},
"sessions.total": {
"message": "total",
"placeholders": {},
@@ -1299,6 +1390,16 @@
"origin": [["src/components/SettingsControls.tsx", 144]],
"translation": "同じタイトルのセッションを統合"
},
"system_info.unknown": {
"message": "Unknown",
"placeholders": {},
"comments": [],
"origin": [
["src/components/SystemInfoCard.tsx", 151],
["src/components/SystemInfoCard.tsx", 163]
],
"translation": "不明"
},
"user.content.unsupported_media": {
"message": "Unsupported Media",
"placeholders": {},
@@ -1323,6 +1424,30 @@
],
"translation": "ユーザーがアップロードした画像コンテンツ"
},
"system_info.feature.agent_sdk.description": {
"message": "Uses Claude Agent SDK instead of Claude Code SDK (v1.0.125+)",
"placeholders": {},
"comments": [],
"origin": [["src/components/SystemInfoCard.tsx", 71]],
"translation": "Claude Code SDKではなくClaude Agent SDKを使用 (v1.0.125+)"
},
"system_info.version_label": {
"message": "Version",
"placeholders": {},
"comments": [],
"origin": [
["src/components/SystemInfoCard.tsx", 130],
["src/components/SystemInfoCard.tsx", 159]
],
"translation": "バージョン"
},
"system_info.description": {
"message": "Version and feature information",
"placeholders": {},
"comments": [],
"origin": [["src/components/SystemInfoCard.tsx", 112]],
"translation": "バージョンと機能情報"
},
"project_list.view_conversations": {
"message": "View Conversations",
"placeholders": {},

File diff suppressed because one or more lines are too long

View File

@@ -4,6 +4,7 @@ import type { ControllerResponse } from "../../../lib/effect/toEffectResponse";
import type { InferEffect } from "../../../lib/effect/types";
import { ApplicationContext } from "../../platform/services/ApplicationContext";
import { ProjectRepository } from "../../project/infrastructure/ProjectRepository";
import * as ClaudeCodeVersion from "../models/ClaudeCodeVersion";
import { ClaudeCodeService } from "../services/ClaudeCodeService";
const LayerImpl = Effect.gen(function* () {
@@ -80,9 +81,43 @@ const LayerImpl = Effect.gen(function* () {
} as const satisfies ControllerResponse;
});
const getClaudeCodeMeta = () =>
Effect.gen(function* () {
const config = yield* claudeCodeService.getClaudeCodeMeta();
return {
response: {
executablePath: config.claudeCodeExecutablePath,
version: config.claudeCodeVersion
? ClaudeCodeVersion.versionText(config.claudeCodeVersion)
: null,
},
status: 200,
} as const satisfies ControllerResponse;
});
const getAvailableFeatures = () =>
Effect.gen(function* () {
const features = yield* claudeCodeService.getAvailableFeatures();
const featuresList = Object.entries(features).flatMap(([key, value]) => {
return [
{
name: key as keyof typeof features,
enabled: value,
},
];
});
return {
response: { features: featuresList },
status: 200,
} as const satisfies ControllerResponse;
});
return {
getClaudeCommands,
getMcpListRoute,
getClaudeCodeMeta,
getAvailableFeatures,
};
});

View File

@@ -13,6 +13,21 @@ class ProjectPathNotFoundError extends Data.TaggedError(
const LayerImpl = Effect.gen(function* () {
const projectRepository = yield* ProjectRepository;
const getClaudeCodeMeta = () =>
Effect.gen(function* () {
const config = yield* ClaudeCode.Config;
return config;
});
const getAvailableFeatures = () =>
Effect.gen(function* () {
const config = yield* ClaudeCode.Config;
const features = ClaudeCode.getAvailableFeatures(
config.claudeCodeVersion,
);
return features;
});
const getMcpList = (projectId: string) =>
Effect.gen(function* () {
const { project } = yield* projectRepository.getProject(projectId);
@@ -27,7 +42,9 @@ const LayerImpl = Effect.gen(function* () {
});
return {
getClaudeCodeMeta,
getMcpList,
getAvailableFeatures,
};
});

View File

@@ -5,6 +5,7 @@ import { setCookie } from "hono/cookie";
import { streamSSE } from "hono/streaming";
import prexit from "prexit";
import { z } from "zod";
import packageJson from "../../../package.json" with { type: "json" };
import { ClaudeCodeController } from "../core/claude-code/presentation/ClaudeCodeController";
import { ClaudeCodePermissionController } from "../core/claude-code/presentation/ClaudeCodePermissionController";
import { ClaudeCodeSessionProcessController } from "../core/claude-code/presentation/ClaudeCodeSessionProcessController";
@@ -94,6 +95,12 @@ export const routes = (app: HonoAppType) =>
});
})
.get("/version", async (c) => {
return c.json({
version: packageJson.version,
});
})
/**
* ProjectController Routes
*/
@@ -298,6 +305,26 @@ export const routes = (app: HonoAppType) =>
return response;
})
.get("/cc/meta", async (c) => {
const response = await effectToResponse(
c,
claudeCodeController
.getClaudeCodeMeta()
.pipe(Effect.provide(runtime)),
);
return response;
})
.get("/cc/features", async (c) => {
const response = await effectToResponse(
c,
claudeCodeController
.getAvailableFeatures()
.pipe(Effect.provide(runtime)),
);
return response;
})
/**
* ClaudeCodeSessionProcessController Routes
*/