mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-28 09:23:08 +08:00
feat(terminal-dashboard): add session name (tag) field for grouping
- Add tag/name input field to CliConfigModal with auto-generation
- Auto-generate format: {tool}-{HHmmss} (e.g., gemini-143052)
- Add regenerate button for quick name changes
- Add i18n keys for new fields (en/zh)
Sessions are now grouped by tag in the sidebar for better organization.
This commit is contained in:
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import { FolderOpen } from 'lucide-react';
|
import { FolderOpen, RefreshCw } from 'lucide-react';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { Button } from '@/components/ui/Button';
|
import { Button } from '@/components/ui/Button';
|
||||||
import { Input } from '@/components/ui/Input';
|
import { Input } from '@/components/ui/Input';
|
||||||
@@ -37,6 +37,8 @@ export interface CliSessionConfig {
|
|||||||
launchMode: LaunchMode;
|
launchMode: LaunchMode;
|
||||||
preferredShell: ShellKind;
|
preferredShell: ShellKind;
|
||||||
workingDir: string;
|
workingDir: string;
|
||||||
|
/** Session tag for grouping (auto-generated if not provided) */
|
||||||
|
tag: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CliConfigModalProps {
|
export interface CliConfigModalProps {
|
||||||
@@ -58,6 +60,16 @@ const MODEL_OPTIONS: Record<CliTool, string[]> = {
|
|||||||
|
|
||||||
const AUTO_MODEL_VALUE = '__auto__';
|
const AUTO_MODEL_VALUE = '__auto__';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a tag name: {tool}-{HHmmss}
|
||||||
|
* Example: gemini-143052
|
||||||
|
*/
|
||||||
|
function generateTag(tool: CliTool): string {
|
||||||
|
const now = new Date();
|
||||||
|
const time = now.toTimeString().slice(0, 8).replace(/:/g, '');
|
||||||
|
return `${tool}-${time}`;
|
||||||
|
}
|
||||||
|
|
||||||
export function CliConfigModal({
|
export function CliConfigModal({
|
||||||
isOpen,
|
isOpen,
|
||||||
onClose,
|
onClose,
|
||||||
@@ -74,19 +86,34 @@ export function CliConfigModal({
|
|||||||
typeof navigator !== 'undefined' && navigator.platform.toLowerCase().includes('win') ? 'cmd' : 'bash'
|
typeof navigator !== 'undefined' && navigator.platform.toLowerCase().includes('win') ? 'cmd' : 'bash'
|
||||||
);
|
);
|
||||||
const [workingDir, setWorkingDir] = React.useState<string>(defaultWorkingDir ?? '');
|
const [workingDir, setWorkingDir] = React.useState<string>(defaultWorkingDir ?? '');
|
||||||
|
const [tag, setTag] = React.useState<string>('');
|
||||||
|
|
||||||
const [isSubmitting, setIsSubmitting] = React.useState(false);
|
const [isSubmitting, setIsSubmitting] = React.useState(false);
|
||||||
const [error, setError] = React.useState<string | null>(null);
|
const [error, setError] = React.useState<string | null>(null);
|
||||||
|
|
||||||
const modelOptions = React.useMemo(() => MODEL_OPTIONS[tool] ?? [], [tool]);
|
const modelOptions = React.useMemo(() => MODEL_OPTIONS[tool] ?? [], [tool]);
|
||||||
|
|
||||||
|
// Generate new tag when modal opens or tool changes
|
||||||
|
const regenerateTag = React.useCallback(() => {
|
||||||
|
setTag(generateTag(tool));
|
||||||
|
}, [tool]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (!isOpen) return;
|
if (!isOpen) return;
|
||||||
// Reset to a safe default each time the modal is opened.
|
// Reset to a safe default each time the modal is opened.
|
||||||
const nextWorkingDir = defaultWorkingDir ?? '';
|
const nextWorkingDir = defaultWorkingDir ?? '';
|
||||||
setWorkingDir(nextWorkingDir);
|
setWorkingDir(nextWorkingDir);
|
||||||
setError(null);
|
setError(null);
|
||||||
}, [isOpen, defaultWorkingDir]);
|
regenerateTag();
|
||||||
|
}, [isOpen, defaultWorkingDir, regenerateTag]);
|
||||||
|
|
||||||
|
// Update tag prefix when tool changes
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (tag) {
|
||||||
|
const suffix = tag.split('-').pop() || '';
|
||||||
|
setTag(`${tool}-${suffix}`);
|
||||||
|
}
|
||||||
|
}, [tool]);
|
||||||
|
|
||||||
const handleToolChange = (nextTool: string) => {
|
const handleToolChange = (nextTool: string) => {
|
||||||
const next = nextTool as CliTool;
|
const next = nextTool as CliTool;
|
||||||
@@ -109,6 +136,8 @@ export function CliConfigModal({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const finalTag = tag.trim() || generateTag(tool);
|
||||||
|
|
||||||
setIsSubmitting(true);
|
setIsSubmitting(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
try {
|
try {
|
||||||
@@ -118,6 +147,7 @@ export function CliConfigModal({
|
|||||||
launchMode,
|
launchMode,
|
||||||
preferredShell,
|
preferredShell,
|
||||||
workingDir: dir,
|
workingDir: dir,
|
||||||
|
tag: finalTag,
|
||||||
});
|
});
|
||||||
onClose();
|
onClose();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -139,6 +169,35 @@ export function CliConfigModal({
|
|||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<div className="space-y-4 py-4">
|
<div className="space-y-4 py-4">
|
||||||
|
{/* Tag / Name */}
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="cli-config-tag">
|
||||||
|
{formatMessage({ id: 'terminalDashboard.cliConfig.tag', defaultMessage: 'Session Name' })}
|
||||||
|
</Label>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Input
|
||||||
|
id="cli-config-tag"
|
||||||
|
value={tag}
|
||||||
|
onChange={(e) => setTag(e.target.value)}
|
||||||
|
placeholder={formatMessage({ id: 'terminalDashboard.cliConfig.tagPlaceholder', defaultMessage: 'e.g., gemini-143052' })}
|
||||||
|
disabled={isSubmitting}
|
||||||
|
className="flex-1"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="outline"
|
||||||
|
onClick={regenerateTag}
|
||||||
|
disabled={isSubmitting}
|
||||||
|
title={formatMessage({ id: 'terminalDashboard.cliConfig.regenerateTag', defaultMessage: 'Regenerate name' })}
|
||||||
|
>
|
||||||
|
<RefreshCw className="w-4 h-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
{formatMessage({ id: 'terminalDashboard.cliConfig.tagHint', defaultMessage: 'Auto-generated as {tool}-{time}. Used for grouping sessions.' })}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||||
{/* Tool */}
|
{/* Tool */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
@@ -271,4 +330,3 @@ export function CliConfigModal({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default CliConfigModal;
|
export default CliConfigModal;
|
||||||
|
|
||||||
|
|||||||
@@ -90,6 +90,10 @@
|
|||||||
"cliConfig": {
|
"cliConfig": {
|
||||||
"title": "Create CLI Session",
|
"title": "Create CLI Session",
|
||||||
"description": "Configure tool, model, mode, shell, and working directory.",
|
"description": "Configure tool, model, mode, shell, and working directory.",
|
||||||
|
"tag": "Session Name",
|
||||||
|
"tagPlaceholder": "e.g., gemini-143052",
|
||||||
|
"tagHint": "Auto-generated as {tool}-{time}. Used for grouping sessions.",
|
||||||
|
"regenerateTag": "Regenerate name",
|
||||||
"tool": "Tool",
|
"tool": "Tool",
|
||||||
"model": "Model",
|
"model": "Model",
|
||||||
"modelAuto": "Auto",
|
"modelAuto": "Auto",
|
||||||
|
|||||||
@@ -90,6 +90,10 @@
|
|||||||
"cliConfig": {
|
"cliConfig": {
|
||||||
"title": "创建 CLI 会话",
|
"title": "创建 CLI 会话",
|
||||||
"description": "配置工具、模型、模式、Shell 与工作目录。",
|
"description": "配置工具、模型、模式、Shell 与工作目录。",
|
||||||
|
"tag": "会话名称",
|
||||||
|
"tagPlaceholder": "例如:gemini-143052",
|
||||||
|
"tagHint": "自动生成格式:{工具}-{时间}。用于会话分组显示。",
|
||||||
|
"regenerateTag": "重新生成名称",
|
||||||
"tool": "工具",
|
"tool": "工具",
|
||||||
"model": "模型",
|
"model": "模型",
|
||||||
"modelAuto": "自动",
|
"modelAuto": "自动",
|
||||||
|
|||||||
Reference in New Issue
Block a user