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:
catlog22
2026-02-20 21:56:47 +08:00
parent f8ff9eaa7f
commit 7e5d47fe8d
3 changed files with 69 additions and 3 deletions

View File

@@ -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;

View File

@@ -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",

View File

@@ -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": "自动",