feat: improve session naming, project display, rename support, tab titles
Backend:
- sessions.py: include workspace_name in session response
- instance_service.py: auto-generate display names as
'Project / Workspace / Tool #N' instead of 'Workspace / Tool #N'
- instance_service.py: add rename_tool_instance() service function
- tool_instances.py: add PATCH /instances/{id} endpoint for renaming
display_name
Frontend:
- api/sessions.ts: add workspace_name to Session type, add renameInstance()
- use-instance-actions.ts: add handleRename, set document.title when opening
- session-card.tsx: click-to-edit display_name inline; always show project
context line (Project / Workspace or Repo / Tool)
- session-list.tsx: pass through onRename prop
- SessionsPage.tsx: wire handleRename to SessionCard and SessionList
- app-shell.tsx: sidebar tooltip includes workspace or repo name
- use-terminal-page.ts: set document.title based on active terminal session
Quality gates: py_compile all backend files pass, tsc --noEmit pass,
npm run build pass, 82/82 tests pass
This commit is contained in:
@@ -12,6 +12,7 @@ export interface SessionCardProps {
|
||||
onStop?: (session: Session) => void;
|
||||
onDelete?: (session: Session) => void;
|
||||
onRecreateTunnel?: (session: Session) => void;
|
||||
onRename?: (session: Session, newName: string) => void;
|
||||
isBusy?: boolean;
|
||||
tunnelHealth?: {
|
||||
healthy: boolean;
|
||||
@@ -43,12 +44,15 @@ export function SessionCard({
|
||||
onStop,
|
||||
onDelete,
|
||||
onRecreateTunnel,
|
||||
onRename,
|
||||
isBusy = false,
|
||||
tunnelHealth = null,
|
||||
}: SessionCardProps) {
|
||||
const [showStopConfirm, setShowStopConfirm] = useState(false);
|
||||
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
|
||||
const [showActionSheet, setShowActionSheet] = useState(false);
|
||||
const [isEditingName, setIsEditingName] = useState(false);
|
||||
const [editName, setEditName] = useState(session.display_name);
|
||||
const isMobile = useMobileViewport();
|
||||
|
||||
const status = statusConfig[session.status] || {
|
||||
@@ -103,7 +107,43 @@ export function SessionCard({
|
||||
<div className="session-card-content">
|
||||
<div className="session-card-header">
|
||||
<div className="session-card-title">
|
||||
<h4>{session.display_name}</h4>
|
||||
{isEditingName ? (
|
||||
<div className="session-card-rename">
|
||||
<input
|
||||
type="text"
|
||||
value={editName}
|
||||
onChange={(e) => setEditName(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
onRename?.(session, editName);
|
||||
setIsEditingName(false);
|
||||
}
|
||||
if (e.key === "Escape") {
|
||||
setEditName(session.display_name);
|
||||
setIsEditingName(false);
|
||||
}
|
||||
}}
|
||||
onBlur={() => {
|
||||
if (editName.trim() && editName !== session.display_name) {
|
||||
onRename?.(session, editName);
|
||||
}
|
||||
setIsEditingName(false);
|
||||
}}
|
||||
autoFocus
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<h4
|
||||
onClick={() => {
|
||||
setEditName(session.display_name);
|
||||
setIsEditingName(true);
|
||||
}}
|
||||
title="Click to rename"
|
||||
style={{ cursor: "pointer" }}
|
||||
>
|
||||
{session.display_name}
|
||||
</h4>
|
||||
)}
|
||||
<div className="session-card-status-badges">
|
||||
<span className={`status-badge ${status.color}`}>
|
||||
{status.label}
|
||||
@@ -118,10 +158,22 @@ export function SessionCard({
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<p className="muted session-card-meta">
|
||||
{session.tool_type_name}
|
||||
{session.project_name && ` · ${session.project_name}`}
|
||||
{session.repository_name && ` · ${session.repository_name}`}
|
||||
<p className="muted session-card-meta session-card-context">
|
||||
<strong>{session.project_name}</strong>
|
||||
{session.workspace_name && (
|
||||
<>
|
||||
{" "}
|
||||
/ <span>{session.workspace_name}</span>
|
||||
</>
|
||||
)}
|
||||
{!session.workspace_name && session.repository_name && (
|
||||
<>
|
||||
{" "}
|
||||
/ <span>{session.repository_name}</span>
|
||||
</>
|
||||
)}
|
||||
{" "}
|
||||
· {session.tool_type_name}
|
||||
</p>
|
||||
{session.clone_mode && (
|
||||
<p className="muted session-card-meta">
|
||||
|
||||
@@ -9,6 +9,7 @@ export interface SessionListProps {
|
||||
onStop?: (session: Session) => void;
|
||||
onDelete?: (session: Session) => void;
|
||||
onRecreateTunnel?: (session: Session) => void;
|
||||
onRename?: (session: Session, newName: string) => void;
|
||||
actionBusyId?: string | null;
|
||||
tunnelHealth?: Record<string, InstanceHealth>;
|
||||
showGrouping?: boolean;
|
||||
@@ -28,6 +29,7 @@ export function SessionList({
|
||||
onStop,
|
||||
onDelete,
|
||||
onRecreateTunnel,
|
||||
onRename,
|
||||
actionBusyId = null,
|
||||
tunnelHealth = {},
|
||||
showGrouping = true,
|
||||
@@ -56,6 +58,7 @@ export function SessionList({
|
||||
onStop={onStop}
|
||||
onDelete={onDelete}
|
||||
onRecreateTunnel={onRecreateTunnel}
|
||||
onRename={onRename}
|
||||
isBusy={actionBusyId === session.id}
|
||||
tunnelHealth={tunnelHealth[session.id] || null}
|
||||
/>
|
||||
@@ -88,6 +91,7 @@ export function SessionList({
|
||||
onStop={onStop}
|
||||
onDelete={onDelete}
|
||||
onRecreateTunnel={onRecreateTunnel}
|
||||
onRename={onRename}
|
||||
isBusy={actionBusyId === session.id}
|
||||
tunnelHealth={tunnelHealth[session.id] || null}
|
||||
/>
|
||||
@@ -113,6 +117,7 @@ export function SessionList({
|
||||
onStop={onStop}
|
||||
onDelete={onDelete}
|
||||
onRecreateTunnel={onRecreateTunnel}
|
||||
onRename={onRename}
|
||||
isBusy={actionBusyId === session.id}
|
||||
tunnelHealth={tunnelHealth[session.id] || null}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user