feat(app): rename a workspace by double-clicking its name

Double-click a sidebar workspace name to edit it inline; Enter/blur commits
via setWorkspaceMeta({name}) (empty/unchanged is a no-op), Esc cancels. The
input stops pointer/key propagation so it doesn't trigger select or drag.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-15 11:47:21 +07:00
parent 99a916fed6
commit a929c166a3
+36 -1
View File
@@ -41,6 +41,7 @@ export function Sidebar({
const [hovered, setHovered] = useState<string | null>(null);
const [drag, setDrag] = useState<{ id: string; section: string } | null>(null);
const [dropAt, setDropAt] = useState<DropAt | null>(null);
const [editing, setEditing] = useState<{ id: string; draft: string } | null>(null);
const dragRef = useRef<{ id: string; section: string } | null>(null);
const dropRef = useRef<DropAt | null>(null);
const [, setTick] = useState(0);
@@ -59,6 +60,17 @@ export function Sidebar({
const togglePin = (w: WorkspaceView) => { void setWorkspaceMeta(w.id, { pinned: !w.pinned }); };
const commitRename = () => {
setEditing((cur) => {
if (cur) {
const name = cur.draft.trim();
const w = workspaces.find((x) => x.id === cur.id);
if (name && w && name !== w.name) void setWorkspaceMeta(cur.id, { name });
}
return null;
});
};
// Persist a new ordering for one section by reassigning sequential `order`
// values (per-section; values never compared across sections).
const commitReorder = (items: WorkspaceView[], fromId: string, toIndex: number) => {
@@ -123,7 +135,30 @@ export function Sidebar({
color: isActive ? COLORS.textPrimary : COLORS.textSecondary,
}}>
<span style={{ width: 10, height: 10, borderRadius: "50%", border: `2px solid ${STATE_COLOR[aggregate(w)]}`, boxSizing: "border-box", flex: "0 0 10px" }} />
<span style={{ flex: 1, fontWeight: isActive ? 600 : 400, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{w.name}</span>
{editing?.id === w.id ? (
<input
autoFocus
value={editing.draft}
onFocus={(e) => e.target.select()}
onMouseDown={(e) => e.stopPropagation()}
onClick={(e) => e.stopPropagation()}
onChange={(e) => setEditing({ id: w.id, draft: e.target.value })}
onBlur={commitRename}
onKeyDown={(e) => {
e.stopPropagation();
if (e.key === "Enter") { e.preventDefault(); commitRename(); }
else if (e.key === "Escape") { e.preventDefault(); setEditing(null); }
}}
style={{ flex: 1, minWidth: 0, background: COLORS.bgApp, color: COLORS.textPrimary, border: `1px solid ${COLORS.accent}`, borderRadius: 4, padding: "2px 6px", fontFamily: FONT.ui, fontSize: 13, outline: "none" }}
/>
) : (
<span
onDoubleClick={(e) => { e.stopPropagation(); setEditing({ id: w.id, draft: w.name }); }}
title="Двойной клик — переименовать"
style={{ flex: 1, fontWeight: isActive ? 600 : 400, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>
{w.name}
</span>
)}
{(hovered === w.id || w.pinned) && (
<Star size={14} fill={w.pinned ? COLORS.stWait : "none"} color={w.pinned ? COLORS.stWait : COLORS.textMuted}
style={{ cursor: "pointer", flex: "0 0 14px" }}