Skip to content

Commit 124efd3

Browse files
committed
Add tab context menu and switch terminal tabs to id-based selection
1 parent 6376862 commit 124efd3

10 files changed

Lines changed: 471 additions & 128 deletions

File tree

anycode/App.tsx

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -296,10 +296,24 @@ const App: React.FC = () => {
296296
agents.setAgentsVersion((prev) => prev + 1);
297297
}, [agents.setAgentsVersion]);
298298

299-
const handleTerminalTabSelect = useCallback((index: number) => {
300-
terminalPanes.selectTab(index);
299+
const activeTerminalId = useMemo(() => {
300+
const paneId = terminalPanes.activePaneId || 'terminal';
301+
return terminalPanes.getSelectedId(paneId);
301302
}, [terminalPanes]);
302303

304+
const handleTerminalTabSelect = useCallback((terminalId: string) => {
305+
terminalPanes.selectTab(terminalId);
306+
}, [terminalPanes]);
307+
308+
const handleTerminalTabClose = useCallback((terminalId: string) => {
309+
terminalPanes.closeTab(terminalId);
310+
}, [terminalPanes]);
311+
312+
const activeToolbarAgentId = (
313+
agentPanes.getSelectedId(agentPanes.activePaneId || 'agent')
314+
?? agents.selectedAgentId
315+
);
316+
303317
const renderPanel = (panelId: PanelId, panelKey: string) => {
304318
switch (panelId) {
305319
case 'files':
@@ -395,13 +409,13 @@ const App: React.FC = () => {
395409
files={editors.files}
396410
activeFileId={editors.activeFileId}
397411
terminals={terminals.terminals}
398-
activeTerminalIndex={terminalPanes.getSelectedIndex(terminalPanes.activePaneId || 'terminal')}
412+
activeTerminalId={activeTerminalId}
399413
agentSessions={sessionsArray}
400-
activeAgentId={agentPanes.getSelectedId(agentPanes.activePaneId || 'agent')}
414+
activeAgentId={activeToolbarAgentId}
401415
onSelectFile={handleSelectFile}
402416
onCloseFile={editors.closeFile}
403417
onSelectTerminal={handleTerminalTabSelect}
404-
onCloseTerminal={terminalPanes.closeTab}
418+
onCloseTerminal={handleTerminalTabClose}
405419
onSelectAgent={agentPanes.selectFromToolbar}
406420
onCloseAgent={agents.closeAgent}
407421
/>

anycode/components/terminal/TerminalEmptyPane.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import './TerminalEmptyPane.css';
44

55
interface TerminalEmptyPaneProps {
66
terminals: Terminal[];
7-
onSelectTerminal: (index: number) => void;
8-
onCloseTerminal: (index: number) => void;
7+
onSelectTerminal: (terminalId: string) => void;
8+
onCloseTerminal: (terminalId: string) => void;
99
onCreateTerminal: () => void;
1010
}
1111

@@ -21,13 +21,13 @@ export const TerminalEmptyPane: React.FC<TerminalEmptyPaneProps> = ({
2121
<div className="terminal-pane-opened">
2222
<div className="terminal-pane-title">Opened terminals</div>
2323
<div className="terminal-pane-actions">
24-
{terminals.map((terminal, index) => (
24+
{terminals.map((terminal) => (
2525
<div key={terminal.id} className="terminal-pane-opened-item">
2626
<button
2727
className="terminal-pane-action terminal-pane-opened-action"
2828
onClick={(event) => {
2929
event.stopPropagation();
30-
onSelectTerminal(index);
30+
onSelectTerminal(terminal.id);
3131
}}
3232
title={terminal.name}
3333
type="button"
@@ -38,7 +38,7 @@ export const TerminalEmptyPane: React.FC<TerminalEmptyPaneProps> = ({
3838
className="tab-close-button terminal-pane-close-button"
3939
onClick={(event) => {
4040
event.stopPropagation();
41-
onCloseTerminal(index);
41+
onCloseTerminal(terminal.id);
4242
}}
4343
title={`Close ${terminal.name}`}
4444
type="button"
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
.tab-context-menu {
2+
position: fixed;
3+
z-index: 10000;
4+
min-width: 196px;
5+
max-height: 240px;
6+
overflow-y: auto;
7+
padding: 2px 8px;
8+
background-color: rgba(28, 28, 28, 0.52);
9+
background-image: linear-gradient(
10+
160deg,
11+
rgba(255, 255, 255, 0.05),
12+
rgba(255, 255, 255, 0.015)
13+
);
14+
border: 1px solid rgba(255, 255, 255, 0.18);
15+
border-radius: 12px;
16+
box-shadow: 0 10px 28px rgba(0, 0, 0, 0.34);
17+
backdrop-filter: blur(10px) saturate(130%);
18+
-webkit-backdrop-filter: blur(10px) saturate(130%);
19+
box-sizing: border-box;
20+
cursor: pointer;
21+
}
22+
23+
.tab-context-menu button {
24+
display: flex;
25+
width: 100%;
26+
align-items: center;
27+
background: transparent;
28+
border: none;
29+
border-radius: 10px;
30+
color: #f3f6fb;
31+
cursor: pointer;
32+
font: inherit;
33+
line-height: 20px;
34+
padding: 2px 8px;
35+
text-align: left;
36+
}
37+
38+
.tab-context-menu button:hover:not(:disabled),
39+
.tab-context-menu button:focus-visible:not(:disabled) {
40+
background-color: rgba(130, 130, 130, 0.33);
41+
}
42+
43+
.tab-context-menu button:disabled {
44+
color: rgba(214, 223, 238, 0.42);
45+
cursor: default;
46+
}
47+
48+
.tab-context-menu-separator {
49+
height: 1px;
50+
margin: 4px 8px;
51+
background: rgba(255, 255, 255, 0.14);
52+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import type { RefObject } from 'react';
2+
import './TabContextMenu.css';
3+
4+
export type TabMenuAction = {
5+
key: string;
6+
label: string;
7+
disabled?: boolean;
8+
onClick: () => void;
9+
};
10+
11+
type TabContextMenuProps = {
12+
anchor: [number, number];
13+
groups: TabMenuAction[][];
14+
onClose: () => void;
15+
menuRef: RefObject<HTMLDivElement | null>;
16+
};
17+
18+
export const TabContextMenu = ({
19+
anchor,
20+
groups,
21+
onClose,
22+
menuRef,
23+
}: TabContextMenuProps) => (
24+
<div
25+
ref={menuRef}
26+
className="tab-context-menu"
27+
style={{ left: anchor[0], top: anchor[1] }}
28+
role="menu"
29+
onContextMenu={(event) => event.preventDefault()}
30+
>
31+
{groups.map((actions, groupIndex) => (
32+
<div key={actions[0]?.key ?? groupIndex}>
33+
{groupIndex > 0 && <div className="tab-context-menu-separator" />}
34+
{actions.map((action) => (
35+
<button
36+
key={action.key}
37+
type="button"
38+
role="menuitem"
39+
disabled={action.disabled}
40+
onClick={() => {
41+
action.onClick();
42+
onClose();
43+
}}
44+
>
45+
{action.label}
46+
</button>
47+
))}
48+
</div>
49+
))}
50+
</div>
51+
);

0 commit comments

Comments
 (0)