Skip to content

Commit 38b7830

Browse files
committed
Refine ACP chat UI grouping and input controls
1 parent 2c12b5c commit 38b7830

9 files changed

Lines changed: 408 additions & 124 deletions

File tree

anycode-backend/config.toml

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ lsp = ["uvx", "ty", "server"]
3131
indent = { width = 4, unit = " " }
3232
executable = true
3333
exec = "python -u {file}"
34-
exectest = "python -m pytest -k {test} {file}"
34+
exectest = "python -m pytest -k {test} {file}"
3535

3636
[[language]]
3737
name = "javascript"
@@ -41,7 +41,7 @@ lsp = ["typescript-language-server", "--stdio"]
4141
indent = { width = 2, unit = " " }
4242
executable = true
4343
exec = "tsx {file}"
44-
exectest = "tsx -m pytest -k {test} {file}"
44+
exectest = "tsx -m pytest -k {test} {file}"
4545

4646
[[language]]
4747
name = "typescript"
@@ -51,7 +51,7 @@ lsp = ["typescript-language-server", "--stdio"]
5151
indent = { width = 2, unit = " " }
5252
executable = true
5353
exec = "tsx {file}"
54-
exectest = "tsx -m pytest -k {test} {file}"
54+
exectest = "tsx -m pytest -k {test} {file}"
5555

5656
[[language]]
5757
name = "javascriptreact"
@@ -71,7 +71,7 @@ lsp = ["typescript-language-server", "--stdio"]
7171
indent = { width = 2, unit = " " }
7272
executable = true
7373
exec = "tsx {file}"
74-
exectest = "tsx -m pytest -k {test} {file}"
74+
exectest = "tsx -m pytest -k {test} {file}"
7575

7676
[[language]]
7777
name = "css"
@@ -139,8 +139,7 @@ exec = "bash {file}"
139139
name = "csharp"
140140
types = [".cs"]
141141
comment = "//"
142-
lsp = ["dotnet", "/opt/omnisharp/OmniSharp.dll", "--languageserver"]
143-
# lsp = ["/usr/run", "--languageserver"]
142+
lsp = ["roslyn-language-server", "--stdio", "--autoLoadProjects"]
144143
indent = { width = 4, unit = " " }
145144
executable = true
146145
exec = "dotnet run {file}"

anycode/components/agent/AcpDialog.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,10 +332,12 @@ const AcpDialogComponent: React.FC<AcpDialogProps> = ({
332332
const session = node.sessionId ? sessionMap.get(node.sessionId) ?? null : null;
333333

334334
if (!session) {
335+
const isActiveEmptyPane = hasMultiplePanes && node.id === activePaneId;
336+
335337
return (
336338
<div
337339
key={node.id}
338-
className={`acp-pane-empty ${node.id === activePaneId ? 'active' : ''}`}
340+
className={`acp-pane-empty ${isActiveEmptyPane ? 'active' : ''}`}
339341
onMouseDown={() => setActivePaneId(node.id)}
340342
>
341343
<div className="acp-pane-empty-title">Empty pane</div>

anycode/components/agent/AcpIcons.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,21 @@ export const AcpIcons = {
5858
<path d="M5 5L15 15M15 5L5 15" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/>
5959
</svg>
6060
),
61+
ChevronRight: () => (
62+
<svg width="12" height="12" viewBox="0 0 16 16" fill="none">
63+
<path d="M6 3L11 8L6 13" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
64+
</svg>
65+
),
66+
ChevronDown: () => (
67+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
68+
<path d="M4 6L8 10L12 6" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
69+
</svg>
70+
),
71+
ChevronUp: () => (
72+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
73+
<path d="M4 10L8 6L12 10" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
74+
</svg>
75+
),
6176
Diff: () => (
6277
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
6378
<path d="M12.5 6.75a.75.75 0 00-1.5 0V9H8.75a.75.75 0 000 1.5H11v2.25a.75.75 0 001.5 0V10.5h2.25a.75.75 0 000-1.5H12.5V6.75zM8.75 16a.75.75 0 000 1.5h6a.75.75 0 000-1.5h-6z"/>

anycode/components/agent/AcpInput.css

Lines changed: 89 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,95 @@
11
.acp-dialog-input {
22
display: flex;
33
flex-direction: column;
4-
gap: 8px;
54
padding: 8px 12px;
65
border-top: 1px solid var(--border-color, #333);
76
align-items: stretch;
87
border-radius: 14px;
8+
transition: padding 0.15s ease-in-out;
9+
}
10+
11+
.acp-dialog-input-minimized {
12+
padding: 8px 12px;
13+
cursor: pointer;
14+
}
15+
16+
.acp-input-preview-container {
17+
display: grid;
18+
grid-template-rows: 0fr;
19+
transition: grid-template-rows 0.15s ease-in-out, opacity 0.15s ease-in-out;
20+
opacity: 0;
21+
pointer-events: none;
22+
}
23+
24+
.acp-dialog-input-minimized .acp-input-preview-container {
25+
grid-template-rows: 1fr;
26+
opacity: 1;
27+
pointer-events: auto;
28+
}
29+
30+
.acp-input-preview-content {
31+
min-height: 0;
32+
overflow: hidden;
33+
}
34+
35+
.acp-input-preview-row {
36+
display: flex;
37+
align-items: center;
38+
justify-content: space-between;
39+
gap: 8px;
40+
width: 100%;
41+
}
42+
43+
.acp-input-full-container {
44+
display: grid;
45+
grid-template-rows: 1fr;
46+
transition: grid-template-rows 0.15s ease-in-out, opacity 0.15s ease-in-out;
47+
opacity: 1;
48+
pointer-events: auto;
49+
}
50+
51+
.acp-dialog-input-minimized .acp-input-full-container {
52+
grid-template-rows: 0fr;
53+
opacity: 0;
54+
pointer-events: none;
55+
}
56+
57+
.acp-input-full-content {
58+
min-height: 0;
59+
overflow: hidden;
60+
display: flex;
61+
flex-direction: column;
62+
gap: 8px;
63+
}
64+
65+
.acp-input-preview-text {
66+
flex: 1;
67+
font-weight: 500;
68+
font-size: 14px;
69+
line-height: 1.5;
70+
color: rgba(255, 255, 255, 0.7);
71+
white-space: nowrap;
72+
overflow: hidden;
73+
text-overflow: ellipsis;
74+
user-select: none;
75+
}
76+
77+
.acp-input-toggle-btn {
78+
background: transparent;
79+
border: none;
80+
color: rgba(255, 255, 255, 0.5);
81+
padding: 4px;
82+
display: flex;
83+
align-items: center;
84+
justify-content: center;
85+
cursor: pointer;
86+
border-radius: 4px;
87+
transition: color 0.2s, background-color 0.2s;
88+
}
89+
90+
.acp-input-toggle-btn:hover {
91+
color: #fff;
92+
background-color: rgba(255, 255, 255, 0.1);
993
}
1094

1195
.acp-input-controls-row {
@@ -109,8 +193,7 @@
109193
padding: 6px 8px;
110194
color: #fff;
111195
-webkit-text-fill-color: #fff;
112-
font-family: inherit;
113-
font-size: 13px;
196+
font-size: 14px;
114197
resize: none;
115198
min-height: 32px;
116199
}
@@ -128,7 +211,7 @@
128211
.acp-dialog-input textarea::placeholder {
129212
color: rgba(255, 255, 255, 0.55);
130213
-webkit-text-fill-color: rgba(255, 255, 255, 0.55);
131-
font-size: 11px;
214+
font-size: 13px;
132215
}
133216

134217
.acp-send-btn,
@@ -191,7 +274,8 @@
191274
.acp-input-select {
192275
}
193276

194-
.acp-dialog-input textarea {
277+
.acp-dialog-input textarea,
278+
.acp-input-preview-text {
195279
font-size: 16px;
196280
}
197281
}

anycode/components/agent/AcpInput.tsx

Lines changed: 83 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ export const AcpInput: React.FC<AcpInputProps> = ({
3939
onSelectModel,
4040
onSelectReasoning,
4141
}) => {
42+
const [isMinimized, setIsMinimized] = React.useState(false);
43+
4244
const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
4345
if (e.key === 'Enter' && !e.shiftKey) {
4446
e.preventDefault();
@@ -102,66 +104,90 @@ export const AcpInput: React.FC<AcpInputProps> = ({
102104
};
103105

104106
return (
105-
<div className="acp-dialog-input">
106-
<div className="acp-input-main-row">
107-
<textarea
108-
id="acp-prompt-input"
109-
name="prompt"
110-
value={value}
111-
onChange={(e) => onChange(e.target.value)}
112-
onKeyDown={handleKeyDown}
113-
placeholder="Ask anything..."
114-
rows={3}
115-
disabled={!isConnected}
116-
/>
117-
{isProcessing ? (
118-
<button
119-
className="acp-stop-prompt-btn"
120-
onClick={onCancel}
121-
disabled={!isConnected}
122-
>
123-
<AcpIcons.Cancel />
124-
</button>
125-
) : (
126-
<button
127-
className="acp-send-btn"
128-
onClick={handleSend}
129-
disabled={!value.trim() || !isConnected}
130-
>
131-
<AcpIcons.Send />
132-
</button>
133-
)}
134-
</div>
135-
{(agentLabel || modelSelector || reasoningSelector || contextUsage) && (
136-
<div className="acp-input-controls-row">
137-
{agentLabel && (
138-
<div className="acp-input-agent-chip" title={agentLabel}>
139-
<span className="acp-input-agent-chip-label">{agentLabel}</span>
140-
{onCloseAgent && (
141-
<button
142-
className="acp-agent-close-btn acp-input-agent-close-btn"
143-
onClick={onCloseAgent}
144-
title="Close agent"
145-
>
146-
<AcpIcons.Close />
147-
</button>
148-
)}
149-
</div>
150-
)}
151-
{renderSelect('acp-model-select', 'model', modelSelector, onSelectModel)}
152-
{renderSelect('acp-reasoning-select', 'thinking', reasoningSelector, onSelectReasoning)}
153-
{contextUsage && (
154-
<div
155-
className="acp-input-context"
156-
title={formatContextTitle(contextUsage.used, contextUsage.size)}
107+
<div className={`acp-dialog-input ${isMinimized ? 'acp-dialog-input-minimized' : ''}`}>
108+
<div className="acp-input-preview-container">
109+
<div className="acp-input-preview-content">
110+
<div className="acp-input-preview-row" onClick={() => setIsMinimized(false)}>
111+
<span className="acp-input-preview-text">{value ? value : "Ask anything..."}</span>
112+
<button
113+
className="acp-input-toggle-btn"
114+
onClick={(e) => { e.stopPropagation(); setIsMinimized(false); }}
115+
title="Expand"
157116
>
158-
<div className="acp-input-context-value">
159-
{formatContextPercent(contextUsage.used, contextUsage.size)}
117+
<AcpIcons.ChevronUp />
118+
</button>
119+
</div>
120+
</div>
121+
</div>
122+
123+
<div className="acp-input-full-container">
124+
<div className="acp-input-full-content">
125+
<div className="acp-input-main-row">
126+
<textarea
127+
id="acp-prompt-input"
128+
name="prompt"
129+
value={value}
130+
onChange={(e) => onChange(e.target.value)}
131+
onKeyDown={handleKeyDown}
132+
placeholder="Ask anything..."
133+
rows={3}
134+
disabled={!isConnected}
135+
/>
136+
{isProcessing ? (
137+
<button
138+
className="acp-stop-prompt-btn"
139+
onClick={onCancel}
140+
disabled={!isConnected}
141+
>
142+
<AcpIcons.Cancel />
143+
</button>
144+
) : (
145+
<button
146+
className="acp-send-btn"
147+
onClick={handleSend}
148+
disabled={!value.trim() || !isConnected}
149+
>
150+
<AcpIcons.Send />
151+
</button>
152+
)}
153+
</div>
154+
<div className="acp-input-controls-row">
155+
{agentLabel && (
156+
<div className="acp-input-agent-chip" title={agentLabel}>
157+
<span className="acp-input-agent-chip-label">{agentLabel}</span>
158+
{onCloseAgent && (
159+
<button
160+
className="acp-agent-close-btn acp-input-agent-close-btn"
161+
onClick={onCloseAgent}
162+
title="Close agent"
163+
>
164+
<AcpIcons.Close />
165+
</button>
166+
)}
167+
</div>
168+
)}
169+
{renderSelect('acp-model-select', 'model', modelSelector, onSelectModel)}
170+
{renderSelect('acp-reasoning-select', 'thinking', reasoningSelector, onSelectReasoning)}
171+
{contextUsage && (
172+
<div
173+
className="acp-input-context"
174+
title={formatContextTitle(contextUsage.used, contextUsage.size)}
175+
>
176+
<div className="acp-input-context-value">
177+
{formatContextPercent(contextUsage.used, contextUsage.size)}
178+
</div>
160179
</div>
161-
</div>
162-
)}
180+
)}
181+
<button
182+
className="acp-input-toggle-btn acp-input-minimize-btn"
183+
onClick={() => setIsMinimized(true)}
184+
title="Minimize"
185+
>
186+
<AcpIcons.ChevronDown />
187+
</button>
188+
</div>
163189
</div>
164-
)}
190+
</div>
165191
</div>
166192
);
167193
};

0 commit comments

Comments
 (0)