codex-linux 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +0 -27
- package/README.md +40 -39
- package/abyss-teal-design-system.html +1449 -0
- package/dist/renderer/assets/main-AJwWHWV7.js +304 -0
- package/dist/renderer/assets/main-ua9RiJ9-.css +1 -0
- package/dist/renderer/index.html +2 -2
- package/package.json +4 -3
- package/scripts/install.sh +1 -1
- package/src/renderer/App.tsx +45 -15
- package/src/renderer/components/AgentPanel.tsx +94 -125
- package/src/renderer/components/AutomationPanel.tsx +39 -34
- package/src/renderer/components/ChatInterface.tsx +81 -123
- package/src/renderer/components/Header.tsx +24 -38
- package/src/renderer/components/SettingsPanel.tsx +89 -96
- package/src/renderer/components/Sidebar.tsx +33 -51
- package/src/renderer/components/SkillsPanel.tsx +54 -56
- package/src/renderer/components/WelcomeChat.tsx +199 -0
- package/src/renderer/components/WorktreePanel.tsx +32 -27
- package/src/renderer/components/ui/Button.tsx +17 -19
- package/src/renderer/components/ui/Card.tsx +14 -15
- package/src/renderer/components/ui/Input.tsx +12 -13
- package/src/renderer/index.css +37 -59
- package/src/renderer/styles/abyss-teal.css +405 -0
- package/dist/renderer/assets/main-DJlZQBCA.js +0 -304
- package/dist/renderer/assets/main-N33ZXEr8.css +0 -1
|
@@ -38,14 +38,13 @@ export const SkillsPanel: React.FC<SkillsPanelProps> = ({
|
|
|
38
38
|
|
|
39
39
|
return (
|
|
40
40
|
<div className="h-full flex">
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
<div className="p-4 border-b border-border">
|
|
41
|
+
<div className="w-80 border-r border-[var(--border-subtle)] bg-[var(--bg-surface)] flex flex-col">
|
|
42
|
+
<div className="p-4 border-b border-[var(--border-faint)]">
|
|
44
43
|
<div className="flex items-center justify-between mb-3">
|
|
45
|
-
<h2 className="font-
|
|
44
|
+
<h2 className="font-medium text-[13px] text-[var(--text-primary)]">Skills Library</h2>
|
|
46
45
|
<button
|
|
47
46
|
onClick={() => setShowCreateModal(true)}
|
|
48
|
-
className="p-2 bg-
|
|
47
|
+
className="p-2 bg-[var(--teal-500)] text-[var(--bg-void)] rounded-[var(--radius-sm)] hover:bg-[var(--teal-400)] transition-colors"
|
|
49
48
|
>
|
|
50
49
|
<Plus className="w-4 h-4" />
|
|
51
50
|
</button>
|
|
@@ -55,31 +54,31 @@ export const SkillsPanel: React.FC<SkillsPanelProps> = ({
|
|
|
55
54
|
placeholder="Search skills..."
|
|
56
55
|
value={searchQuery}
|
|
57
56
|
onChange={e => setSearchQuery(e.target.value)}
|
|
58
|
-
className="w-full px-3 py-2 bg-
|
|
57
|
+
className="w-full px-3 py-2 bg-[var(--bg-elevated)] border border-[var(--border-subtle)] rounded-[var(--radius-md)] text-[12px] text-[var(--text-primary)] placeholder:text-[var(--text-disabled)] focus:outline-none focus:border-[var(--teal-500)]"
|
|
59
58
|
/>
|
|
60
59
|
</div>
|
|
61
60
|
|
|
62
|
-
<div className="flex-1 overflow-auto p-2 space-y-
|
|
61
|
+
<div className="flex-1 overflow-auto p-2 space-y-1">
|
|
63
62
|
{filteredSkills.map(skill => (
|
|
64
63
|
<div
|
|
65
64
|
key={skill.id}
|
|
66
65
|
onClick={() => setSelectedSkill(skill)}
|
|
67
|
-
className={`p-3 rounded-
|
|
66
|
+
className={`p-3 rounded-[var(--radius-md)] cursor-pointer transition-all ${
|
|
68
67
|
selectedSkill?.id === skill.id
|
|
69
|
-
? 'bg-
|
|
70
|
-
: 'hover:bg-
|
|
68
|
+
? 'bg-[rgba(0,200,168,0.08)] border border-[var(--border-accent)]'
|
|
69
|
+
: 'hover:bg-[var(--bg-hover)] border border-transparent'
|
|
71
70
|
}`}
|
|
72
71
|
>
|
|
73
72
|
<div className="flex items-center gap-2">
|
|
74
|
-
<Wrench className="w-4 h-4 text-muted
|
|
75
|
-
<span className="font-medium text-
|
|
73
|
+
<Wrench className="w-4 h-4 text-[var(--text-muted)]" />
|
|
74
|
+
<span className="font-medium text-[13px] text-[var(--text-primary)]">{skill.name}</span>
|
|
76
75
|
</div>
|
|
77
|
-
<p className="mt-1 text-
|
|
76
|
+
<p className="mt-1 text-[11px] text-[var(--text-muted)] line-clamp-2">
|
|
78
77
|
{skill.description}
|
|
79
78
|
</p>
|
|
80
79
|
<div className="flex flex-wrap gap-1 mt-2">
|
|
81
80
|
{skill.tags.slice(0, 3).map(tag => (
|
|
82
|
-
<span key={tag} className="
|
|
81
|
+
<span key={tag} className="badge badge-neutral text-[10px]">
|
|
83
82
|
{tag}
|
|
84
83
|
</span>
|
|
85
84
|
))}
|
|
@@ -88,36 +87,35 @@ export const SkillsPanel: React.FC<SkillsPanelProps> = ({
|
|
|
88
87
|
))}
|
|
89
88
|
|
|
90
89
|
{filteredSkills.length === 0 && (
|
|
91
|
-
<div className="text-center py-8 text-muted
|
|
92
|
-
<Wrench className="w-12 h-12 mx-auto mb-2 opacity-
|
|
93
|
-
<p className="text-
|
|
90
|
+
<div className="text-center py-8 text-[var(--text-muted)]">
|
|
91
|
+
<Wrench className="w-12 h-12 mx-auto mb-2 opacity-30" />
|
|
92
|
+
<p className="text-[13px]">No skills found</p>
|
|
94
93
|
</div>
|
|
95
94
|
)}
|
|
96
95
|
</div>
|
|
97
96
|
</div>
|
|
98
97
|
|
|
99
|
-
{/* Skill Detail */}
|
|
100
98
|
<div className="flex-1 flex flex-col">
|
|
101
99
|
{selectedSkill ? (
|
|
102
100
|
<>
|
|
103
|
-
<div className="p-4 border-b border-border flex items-start justify-between">
|
|
101
|
+
<div className="p-4 border-b border-[var(--border-subtle)] flex items-start justify-between">
|
|
104
102
|
<div>
|
|
105
|
-
<h2 className="text-
|
|
106
|
-
<p className="text-
|
|
103
|
+
<h2 className="text-[16px] font-medium text-[var(--text-primary)]">{selectedSkill.name}</h2>
|
|
104
|
+
<p className="text-[12px] text-[var(--text-muted)]">{selectedSkill.description}</p>
|
|
107
105
|
<div className="flex flex-wrap gap-2 mt-2">
|
|
108
106
|
{selectedSkill.tags.map(tag => (
|
|
109
|
-
<span key={tag} className="
|
|
110
|
-
<Tag className="w-3 h-3
|
|
107
|
+
<span key={tag} className="badge badge-teal">
|
|
108
|
+
<Tag className="w-3 h-3" />
|
|
111
109
|
{tag}
|
|
112
110
|
</span>
|
|
113
111
|
))}
|
|
114
112
|
</div>
|
|
115
113
|
</div>
|
|
116
114
|
<div className="flex gap-2">
|
|
117
|
-
<button className="p-2 hover:bg-
|
|
115
|
+
<button className="p-2 hover:bg-[var(--bg-hover)] rounded-[var(--radius-sm)] text-[var(--text-muted)] transition-colors">
|
|
118
116
|
<Edit2 className="w-4 h-4" />
|
|
119
117
|
</button>
|
|
120
|
-
<button className="p-2 text-
|
|
118
|
+
<button className="p-2 text-[var(--error)] hover:bg-[rgba(232,90,106,0.1)] rounded-[var(--radius-sm)] transition-colors">
|
|
121
119
|
<Trash2 className="w-4 h-4" />
|
|
122
120
|
</button>
|
|
123
121
|
</div>
|
|
@@ -126,22 +124,22 @@ export const SkillsPanel: React.FC<SkillsPanelProps> = ({
|
|
|
126
124
|
<div className="flex-1 overflow-auto p-4">
|
|
127
125
|
<div className="space-y-4">
|
|
128
126
|
<div>
|
|
129
|
-
<h3 className="text-
|
|
127
|
+
<h3 className="text-[12px] font-medium mb-2 text-[var(--text-secondary)]">Files</h3>
|
|
130
128
|
<div className="space-y-2">
|
|
131
129
|
{selectedSkill.files.map(file => (
|
|
132
130
|
<div
|
|
133
131
|
key={file.path}
|
|
134
|
-
className="p-3 bg-
|
|
132
|
+
className="p-3 bg-[var(--bg-card)] border border-[var(--border-subtle)] rounded-[var(--radius-md)]"
|
|
135
133
|
>
|
|
136
134
|
<div className="flex items-center gap-2 mb-2">
|
|
137
|
-
<FileText className="w-4 h-4 text-muted
|
|
138
|
-
<span className="text-
|
|
139
|
-
<span className="text-
|
|
135
|
+
<FileText className="w-4 h-4 text-[var(--text-muted)]" />
|
|
136
|
+
<span className="text-[12px] font-medium text-[var(--text-primary)]">{file.path}</span>
|
|
137
|
+
<span className="text-[10px] text-[var(--text-muted)] uppercase">
|
|
140
138
|
{file.type}
|
|
141
139
|
</span>
|
|
142
140
|
</div>
|
|
143
|
-
<pre className="text-
|
|
144
|
-
<code>{file.content.slice(0, 500)}{file.content.length > 500 ? '...' : ''}</code>
|
|
141
|
+
<pre className="text-[11px] bg-[var(--bg-void)] p-2 rounded-[var(--radius-sm)] overflow-x-auto border border-[var(--border-faint)]">
|
|
142
|
+
<code className="text-[var(--teal-300)]">{file.content.slice(0, 500)}{file.content.length > 500 ? '...' : ''}</code>
|
|
145
143
|
</pre>
|
|
146
144
|
</div>
|
|
147
145
|
))}
|
|
@@ -149,24 +147,24 @@ export const SkillsPanel: React.FC<SkillsPanelProps> = ({
|
|
|
149
147
|
</div>
|
|
150
148
|
|
|
151
149
|
<div>
|
|
152
|
-
<h3 className="text-
|
|
153
|
-
<div className="bg-
|
|
150
|
+
<h3 className="text-[12px] font-medium mb-2 text-[var(--text-secondary)]">Configuration</h3>
|
|
151
|
+
<div className="bg-[var(--bg-card)] border border-[var(--border-subtle)] p-3 rounded-[var(--radius-md)] text-[12px]">
|
|
154
152
|
<div className="grid grid-cols-2 gap-4">
|
|
155
153
|
<div>
|
|
156
|
-
<span className="text-muted
|
|
157
|
-
<span className="ml-2">{selectedSkill.config.entryPoint}</span>
|
|
154
|
+
<span className="text-[var(--text-muted)]">Entry Point:</span>
|
|
155
|
+
<span className="ml-2 text-[var(--text-primary)]">{selectedSkill.config.entryPoint}</span>
|
|
158
156
|
</div>
|
|
159
157
|
<div>
|
|
160
|
-
<span className="text-muted
|
|
161
|
-
<span className="ml-2">{selectedSkill.version}</span>
|
|
158
|
+
<span className="text-[var(--text-muted)]">Version:</span>
|
|
159
|
+
<span className="ml-2 text-[var(--text-primary)]">{selectedSkill.version}</span>
|
|
162
160
|
</div>
|
|
163
161
|
<div>
|
|
164
|
-
<span className="text-muted
|
|
165
|
-
<span className="ml-2">{selectedSkill.author}</span>
|
|
162
|
+
<span className="text-[var(--text-muted)]">Author:</span>
|
|
163
|
+
<span className="ml-2 text-[var(--text-primary)]">{selectedSkill.author}</span>
|
|
166
164
|
</div>
|
|
167
165
|
<div>
|
|
168
|
-
<span className="text-muted
|
|
169
|
-
<span className="ml-2">{selectedSkill.config.dependencies.length || 'None'}</span>
|
|
166
|
+
<span className="text-[var(--text-muted)]">Dependencies:</span>
|
|
167
|
+
<span className="ml-2 text-[var(--text-primary)]">{selectedSkill.config.dependencies.length || 'None'}</span>
|
|
170
168
|
</div>
|
|
171
169
|
</div>
|
|
172
170
|
</div>
|
|
@@ -175,48 +173,48 @@ export const SkillsPanel: React.FC<SkillsPanelProps> = ({
|
|
|
175
173
|
</div>
|
|
176
174
|
</>
|
|
177
175
|
) : (
|
|
178
|
-
<div className="flex-1 flex items-center justify-center text-muted
|
|
176
|
+
<div className="flex-1 flex items-center justify-center text-[var(--text-muted)]">
|
|
179
177
|
<div className="text-center">
|
|
180
178
|
<Wrench className="w-16 h-16 mx-auto mb-4 opacity-30" />
|
|
181
|
-
<p>Select a skill to view details</p>
|
|
179
|
+
<p className="text-[14px]">Select a skill to view details</p>
|
|
182
180
|
</div>
|
|
183
181
|
</div>
|
|
184
182
|
)}
|
|
185
183
|
</div>
|
|
186
184
|
|
|
187
185
|
{showCreateModal && (
|
|
188
|
-
<div className="fixed inset-0 bg-
|
|
189
|
-
<div className="bg-card border border-border rounded-lg p-6 w-[600px]">
|
|
190
|
-
<h2 className="text-
|
|
186
|
+
<div className="fixed inset-0 bg-[rgba(3,7,9,0.8)] flex items-center justify-center z-50">
|
|
187
|
+
<div className="bg-[var(--bg-card)] border border-[var(--border-subtle)] rounded-[var(--radius-lg)] p-6 w-[600px]">
|
|
188
|
+
<h2 className="text-[16px] font-medium text-[var(--text-primary)] mb-4">Create New Skill</h2>
|
|
191
189
|
|
|
192
190
|
<div className="space-y-4">
|
|
193
191
|
<div>
|
|
194
|
-
<label className="block text-
|
|
192
|
+
<label className="block text-[11px] font-medium mb-1 text-[var(--text-secondary)]">Name</label>
|
|
195
193
|
<input
|
|
196
194
|
type="text"
|
|
197
195
|
value={newSkill.name}
|
|
198
196
|
onChange={e => setNewSkill({ ...newSkill, name: e.target.value })}
|
|
199
|
-
className="w-full px-3 py-2 bg-
|
|
197
|
+
className="w-full px-3 py-2 bg-[var(--bg-elevated)] border border-[var(--border-subtle)] rounded-[var(--radius-md)] text-[13px] text-[var(--text-primary)] placeholder:text-[var(--text-disabled)] focus:outline-none focus:border-[var(--teal-500)]"
|
|
200
198
|
placeholder="My Custom Skill"
|
|
201
199
|
/>
|
|
202
200
|
</div>
|
|
203
201
|
|
|
204
202
|
<div>
|
|
205
|
-
<label className="block text-
|
|
203
|
+
<label className="block text-[11px] font-medium mb-1 text-[var(--text-secondary)]">Description</label>
|
|
206
204
|
<textarea
|
|
207
205
|
value={newSkill.description}
|
|
208
206
|
onChange={e => setNewSkill({ ...newSkill, description: e.target.value })}
|
|
209
|
-
className="w-full px-3 py-2 bg-
|
|
207
|
+
className="w-full px-3 py-2 bg-[var(--bg-elevated)] border border-[var(--border-subtle)] rounded-[var(--radius-md)] h-20 resize-none text-[13px] text-[var(--text-primary)] placeholder:text-[var(--text-disabled)] focus:outline-none focus:border-[var(--teal-500)]"
|
|
210
208
|
placeholder="What does this skill do?"
|
|
211
209
|
/>
|
|
212
210
|
</div>
|
|
213
211
|
|
|
214
212
|
<div>
|
|
215
|
-
<label className="block text-
|
|
213
|
+
<label className="block text-[11px] font-medium mb-1 text-[var(--text-secondary)]">Content</label>
|
|
216
214
|
<textarea
|
|
217
215
|
value={newSkill.content}
|
|
218
216
|
onChange={e => setNewSkill({ ...newSkill, content: e.target.value })}
|
|
219
|
-
className="w-full px-3 py-2 bg-
|
|
217
|
+
className="w-full px-3 py-2 bg-[var(--bg-elevated)] border border-[var(--border-subtle)] rounded-[var(--radius-md)] h-40 resize-none font-[var(--font-mono)] text-[12px] text-[var(--teal-300)] placeholder:text-[var(--text-disabled)] focus:outline-none focus:border-[var(--teal-500)]"
|
|
220
218
|
placeholder="Enter skill instructions..."
|
|
221
219
|
/>
|
|
222
220
|
</div>
|
|
@@ -225,14 +223,14 @@ export const SkillsPanel: React.FC<SkillsPanelProps> = ({
|
|
|
225
223
|
<div className="flex justify-end gap-2 mt-6">
|
|
226
224
|
<button
|
|
227
225
|
onClick={() => setShowCreateModal(false)}
|
|
228
|
-
className="px-4 py-2 text-muted-
|
|
226
|
+
className="px-4 py-2 text-[var(--text-muted)] hover:text-[var(--text-primary)] text-[13px] transition-colors"
|
|
229
227
|
>
|
|
230
228
|
Cancel
|
|
231
229
|
</button>
|
|
232
230
|
<button
|
|
233
231
|
onClick={handleCreate}
|
|
234
232
|
disabled={!newSkill.name || !newSkill.content}
|
|
235
|
-
className="px-4 py-2 bg-
|
|
233
|
+
className="px-4 py-2 bg-[var(--teal-500)] text-[var(--bg-void)] rounded-[var(--radius-sm)] disabled:opacity-50 text-[13px] font-medium transition-colors hover:bg-[var(--teal-400)]"
|
|
236
234
|
>
|
|
237
235
|
Create Skill
|
|
238
236
|
</button>
|
|
@@ -242,4 +240,4 @@ export const SkillsPanel: React.FC<SkillsPanelProps> = ({
|
|
|
242
240
|
)}
|
|
243
241
|
</div>
|
|
244
242
|
);
|
|
245
|
-
};
|
|
243
|
+
};
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import React, { useState, useRef } from 'react';
|
|
2
|
+
import { Agent, AIProvider, Skill } from '../../shared/types';
|
|
3
|
+
import { Send, Sparkles, Code2, GitBranch, Wrench, Clock, Bot, X } from 'lucide-react';
|
|
4
|
+
|
|
5
|
+
interface WelcomeChatProps {
|
|
6
|
+
agents: Agent[];
|
|
7
|
+
providers: AIProvider[];
|
|
8
|
+
skills: Skill[];
|
|
9
|
+
onCreateAgent: (config: any) => Promise<Agent>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const FREE_MODELS = [
|
|
13
|
+
{ id: 'meta-llama/llama-3.3-70b-instruct:free', name: 'Llama 3.3 70B', backend: 'openrouter' },
|
|
14
|
+
{ id: 'deepseek/deepseek-r1-0528:free', name: 'DeepSeek R1', backend: 'openrouter' },
|
|
15
|
+
{ id: 'mistralai/mistral-small-3.1-24b-instruct:free', name: 'Mistral Small 3.1', backend: 'openrouter' },
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
export const WelcomeChat: React.FC<WelcomeChatProps> = ({
|
|
19
|
+
agents,
|
|
20
|
+
providers,
|
|
21
|
+
skills,
|
|
22
|
+
onCreateAgent
|
|
23
|
+
}) => {
|
|
24
|
+
const [input, setInput] = useState('');
|
|
25
|
+
const [activeTab, setActiveTab] = useState('Cowork');
|
|
26
|
+
const [selectedModel] = useState(FREE_MODELS[0].id);
|
|
27
|
+
const inputRef = useRef<HTMLTextAreaElement>(null);
|
|
28
|
+
|
|
29
|
+
const suggestions = [
|
|
30
|
+
{ icon: Code2, text: 'Optimize my workflow' },
|
|
31
|
+
{ icon: GitBranch, text: 'Organize my files' },
|
|
32
|
+
{ icon: Wrench, text: 'Find insights in data' },
|
|
33
|
+
{ icon: Clock, text: 'Build an automation' },
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
const files = [
|
|
37
|
+
{ name: 'Project Constitution v4', lines: 491, type: 'TEXT' },
|
|
38
|
+
{ name: 'Task Breakdown', lines: 566, type: 'TEXT' },
|
|
39
|
+
{ name: 'Implementation Plan', lines: 390, type: 'TEXT' },
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
const handleSend = async () => {
|
|
43
|
+
if (!input.trim()) return;
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
const newAgent = await onCreateAgent({
|
|
47
|
+
name: 'Quick Chat',
|
|
48
|
+
projectPath: process.cwd(),
|
|
49
|
+
providerId: 'free',
|
|
50
|
+
model: selectedModel,
|
|
51
|
+
skills: []
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
await window.electronAPI.agent.sendMessage(newAgent.id, input);
|
|
55
|
+
setInput('');
|
|
56
|
+
} catch (error) {
|
|
57
|
+
console.error('Failed to send message:', error);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const handleKeyDown = (e: React.KeyboardEvent) => {
|
|
62
|
+
if (e.key === 'Enter' && !e.shiftKey) {
|
|
63
|
+
e.preventDefault();
|
|
64
|
+
handleSend();
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<div className="h-full flex relative">
|
|
70
|
+
<div
|
|
71
|
+
className="absolute inset-0 pointer-events-none z-0"
|
|
72
|
+
style={{
|
|
73
|
+
backgroundImage: 'radial-gradient(circle, rgba(0,200,168,0.04) 1px, transparent 1px)',
|
|
74
|
+
backgroundSize: '22px 22px'
|
|
75
|
+
}}
|
|
76
|
+
/>
|
|
77
|
+
|
|
78
|
+
<div className="absolute w-[600px] h-[400px] -top-[120px] -right-[100px] pointer-events-none z-0"
|
|
79
|
+
style={{
|
|
80
|
+
background: 'radial-gradient(ellipse, rgba(0,200,168,0.09) 0%, transparent 65%)'
|
|
81
|
+
}}
|
|
82
|
+
/>
|
|
83
|
+
<div className="absolute w-[300px] h-[300px] -bottom-[80px] -left-[60px] pointer-events-none z-0"
|
|
84
|
+
style={{
|
|
85
|
+
background: 'radial-gradient(circle, rgba(0,150,130,0.06) 0%, transparent 70%)'
|
|
86
|
+
}}
|
|
87
|
+
/>
|
|
88
|
+
|
|
89
|
+
<div className="flex-1 flex flex-col z-10">
|
|
90
|
+
<div className="h-12 border-b border-[var(--border-subtle)] flex items-center px-5 gap-1">
|
|
91
|
+
<div className="flex gap-0.5 bg-[var(--bg-elevated)] p-[3px] rounded-lg">
|
|
92
|
+
{['Chat', 'Cowork', 'Code'].map(tab => (
|
|
93
|
+
<button
|
|
94
|
+
key={tab}
|
|
95
|
+
onClick={() => setActiveTab(tab)}
|
|
96
|
+
className={`px-4 py-1.5 rounded-md text-xs font-medium transition-all ${
|
|
97
|
+
activeTab === tab
|
|
98
|
+
? 'bg-[var(--bg-card)] text-[var(--text-primary)] shadow-[0_1px_4px_rgba(0,0,0,0.4)]'
|
|
99
|
+
: 'text-[var(--text-muted)] hover:text-[var(--text-secondary)]'
|
|
100
|
+
}`}
|
|
101
|
+
>
|
|
102
|
+
{tab}
|
|
103
|
+
</button>
|
|
104
|
+
))}
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
<div className="flex-1 p-7 flex flex-col gap-5 overflow-hidden">
|
|
109
|
+
<h1
|
|
110
|
+
className="text-[22px] leading-tight tracking-[-0.01em] text-[var(--text-primary)]"
|
|
111
|
+
style={{ fontFamily: 'var(--font-display)', fontStyle: 'italic', fontWeight: 300 }}
|
|
112
|
+
>
|
|
113
|
+
Let's knock something<br />off your list
|
|
114
|
+
</h1>
|
|
115
|
+
|
|
116
|
+
<div className="grid grid-cols-2 gap-2.5 flex-1">
|
|
117
|
+
{suggestions.map((sugg, idx) => {
|
|
118
|
+
const Icon = sugg.icon;
|
|
119
|
+
return (
|
|
120
|
+
<button
|
|
121
|
+
key={idx}
|
|
122
|
+
onClick={() => setInput(sugg.text)}
|
|
123
|
+
className="flex items-center gap-2.5 px-4 py-3.5 bg-[var(--bg-card)] border border-[var(--border-subtle)] rounded-[var(--radius-md)] text-left hover:border-[var(--border-accent)] transition-colors group"
|
|
124
|
+
>
|
|
125
|
+
<div className="w-7 h-7 rounded-md bg-[rgba(0,200,168,0.08)] border border-[rgba(0,200,168,0.12)] flex items-center justify-center flex-shrink-0">
|
|
126
|
+
<Icon className="w-3.5 h-3.5 text-[var(--teal-400)]" />
|
|
127
|
+
</div>
|
|
128
|
+
<span className="text-[12px] text-[var(--text-secondary)] group-hover:text-[var(--text-primary)] transition-colors">
|
|
129
|
+
{sugg.text}
|
|
130
|
+
</span>
|
|
131
|
+
</button>
|
|
132
|
+
);
|
|
133
|
+
})}
|
|
134
|
+
</div>
|
|
135
|
+
|
|
136
|
+
<div className="bg-[var(--bg-card)] border border-[var(--border-default)] rounded-[var(--radius-lg)] px-3.5 py-3 flex gap-2.5 items-center">
|
|
137
|
+
<textarea
|
|
138
|
+
ref={inputRef}
|
|
139
|
+
value={input}
|
|
140
|
+
onChange={e => setInput(e.target.value)}
|
|
141
|
+
onKeyDown={handleKeyDown}
|
|
142
|
+
placeholder="How can I help you today?"
|
|
143
|
+
className="flex-1 bg-transparent border-none outline-none text-[12px] text-[var(--text-primary)] placeholder:text-[var(--text-disabled)] resize-none leading-normal"
|
|
144
|
+
rows={1}
|
|
145
|
+
/>
|
|
146
|
+
<button
|
|
147
|
+
onClick={handleSend}
|
|
148
|
+
disabled={!input.trim()}
|
|
149
|
+
className="w-7 h-7 rounded-[7px] bg-[var(--teal-500)] flex items-center justify-center disabled:opacity-50 hover:bg-[var(--teal-400)] transition-colors flex-shrink-0"
|
|
150
|
+
>
|
|
151
|
+
<svg width="12" height="12" viewBox="0 0 12 12" fill="var(--bg-void)">
|
|
152
|
+
<path d="M1 6l9-4-3.5 4L10 10 1 6z"/>
|
|
153
|
+
</svg>
|
|
154
|
+
</button>
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
</div>
|
|
158
|
+
|
|
159
|
+
<div className="w-60 bg-[var(--bg-surface)] border-l border-[var(--border-subtle)] p-4 flex flex-col gap-3 z-10 flex-shrink-0">
|
|
160
|
+
<div>
|
|
161
|
+
<div className="text-[10px] font-medium tracking-[0.12em] uppercase text-[var(--text-muted)] mb-1">
|
|
162
|
+
Memory
|
|
163
|
+
</div>
|
|
164
|
+
<div className="bg-[var(--bg-card)] border border-[var(--border-subtle)] rounded-[var(--radius-md)] px-3 py-2.5 text-[11px] text-[var(--text-secondary)] leading-relaxed">
|
|
165
|
+
Purpose & context
|
|
166
|
+
<small className="block text-[var(--text-muted)] text-[10px] mt-0.5">
|
|
167
|
+
Only you · Updated 14 days ago
|
|
168
|
+
</small>
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
171
|
+
|
|
172
|
+
<div>
|
|
173
|
+
<div className="text-[10px] font-medium tracking-[0.12em] uppercase text-[var(--text-muted)] mb-1">
|
|
174
|
+
Files
|
|
175
|
+
</div>
|
|
176
|
+
<div className="flex flex-col gap-2">
|
|
177
|
+
{files.map((file, idx) => (
|
|
178
|
+
<div key={idx} className="bg-[var(--bg-card)] border border-[var(--border-subtle)] rounded-[var(--radius-md)] px-3 py-2.5 text-[11px] text-[var(--text-secondary)] leading-relaxed">
|
|
179
|
+
{file.name}
|
|
180
|
+
<small className="block text-[var(--text-muted)] text-[10px] mt-0.5">
|
|
181
|
+
{file.lines} lines · {file.type}
|
|
182
|
+
</small>
|
|
183
|
+
</div>
|
|
184
|
+
))}
|
|
185
|
+
</div>
|
|
186
|
+
</div>
|
|
187
|
+
|
|
188
|
+
<div className="mt-auto">
|
|
189
|
+
<div className="h-1 bg-[var(--bg-hover)] rounded overflow-hidden">
|
|
190
|
+
<div className="h-full rounded bg-gradient-to-r from-[var(--teal-700)] to-[var(--teal-400)]" style={{ width: '2%' }} />
|
|
191
|
+
</div>
|
|
192
|
+
<div className="text-[9px] text-[var(--text-muted)] mt-1.5">
|
|
193
|
+
2% of project capacity
|
|
194
|
+
</div>
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
197
|
+
</div>
|
|
198
|
+
);
|
|
199
|
+
};
|
|
@@ -29,14 +29,19 @@ export const WorktreePanel: React.FC<WorktreePanelProps> = ({
|
|
|
29
29
|
|
|
30
30
|
return (
|
|
31
31
|
<div className="h-full flex flex-col">
|
|
32
|
-
<div className="p-4 border-b border-border flex items-center justify-between">
|
|
32
|
+
<div className="p-4 border-b border-[var(--border-subtle)] flex items-center justify-between">
|
|
33
33
|
<div>
|
|
34
|
-
<h2
|
|
35
|
-
|
|
34
|
+
<h2
|
|
35
|
+
className="text-[18px] font-medium text-[var(--text-primary)]"
|
|
36
|
+
style={{ fontFamily: 'var(--font-display)', fontStyle: 'italic', fontWeight: 300 }}
|
|
37
|
+
>
|
|
38
|
+
Worktrees
|
|
39
|
+
</h2>
|
|
40
|
+
<p className="text-[12px] text-[var(--text-muted)]">Isolated workspaces for agents</p>
|
|
36
41
|
</div>
|
|
37
42
|
<button
|
|
38
43
|
onClick={() => setShowCreateModal(true)}
|
|
39
|
-
className="flex items-center gap-2 px-4 py-2 bg-
|
|
44
|
+
className="flex items-center gap-2 px-4 py-2 bg-[var(--teal-500)] text-[var(--bg-void)] rounded-[var(--radius-sm)] hover:bg-[var(--teal-400)] text-[13px] font-medium transition-colors"
|
|
40
45
|
>
|
|
41
46
|
<Plus className="w-4 h-4" />
|
|
42
47
|
New Worktree
|
|
@@ -48,21 +53,21 @@ export const WorktreePanel: React.FC<WorktreePanelProps> = ({
|
|
|
48
53
|
{worktrees.map(worktree => (
|
|
49
54
|
<div
|
|
50
55
|
key={worktree.name}
|
|
51
|
-
className="p-4 bg-card border border-border rounded-lg"
|
|
56
|
+
className="p-4 bg-[var(--bg-card)] border border-[var(--border-subtle)] rounded-[var(--radius-lg)] hover:border-[var(--border-accent)] hover:translate-y-[-2px] transition-all"
|
|
52
57
|
>
|
|
53
58
|
<div className="flex items-start justify-between mb-3">
|
|
54
59
|
<div className="flex items-center gap-2">
|
|
55
|
-
<GitBranch className="w-5 h-5 text-muted
|
|
56
|
-
<span className="font-medium">{worktree.name}</span>
|
|
60
|
+
<GitBranch className="w-5 h-5 text-[var(--text-muted)]" />
|
|
61
|
+
<span className="font-medium text-[13px] text-[var(--text-primary)]">{worktree.name}</span>
|
|
57
62
|
</div>
|
|
58
63
|
{worktree.isMain && (
|
|
59
|
-
<span className="
|
|
64
|
+
<span className="badge badge-teal text-[10px]">
|
|
60
65
|
Main
|
|
61
66
|
</span>
|
|
62
67
|
)}
|
|
63
68
|
</div>
|
|
64
69
|
|
|
65
|
-
<div className="space-y-2 text-
|
|
70
|
+
<div className="space-y-2 text-[12px] text-[var(--text-muted)]">
|
|
66
71
|
<div className="flex items-center gap-2">
|
|
67
72
|
<Folder className="w-4 h-4" />
|
|
68
73
|
<span className="truncate">{worktree.path}</span>
|
|
@@ -71,18 +76,18 @@ export const WorktreePanel: React.FC<WorktreePanelProps> = ({
|
|
|
71
76
|
<GitBranch className="w-4 h-4" />
|
|
72
77
|
<span>{worktree.branch}</span>
|
|
73
78
|
</div>
|
|
74
|
-
<div className="text-
|
|
79
|
+
<div className="text-[11px] font-[var(--font-mono)] text-[var(--teal-300)] truncate">
|
|
75
80
|
{worktree.commit?.slice(0, 8)}
|
|
76
81
|
</div>
|
|
77
82
|
</div>
|
|
78
83
|
|
|
79
84
|
{!worktree.isMain && (
|
|
80
|
-
<div className="flex gap-2 mt-4 pt-4 border-t border-border">
|
|
81
|
-
<button className="flex-1 px-3 py-1.5 text-
|
|
85
|
+
<div className="flex gap-2 mt-4 pt-4 border-t border-[var(--border-faint)]">
|
|
86
|
+
<button className="flex-1 px-3 py-1.5 text-[12px] bg-[var(--bg-hover)] rounded-[var(--radius-sm)] hover:bg-[var(--bg-active)] text-[var(--text-secondary)] transition-colors">
|
|
82
87
|
<GitMerge className="w-4 h-4 inline mr-1" />
|
|
83
88
|
Merge
|
|
84
89
|
</button>
|
|
85
|
-
<button className="p-1.5 text-
|
|
90
|
+
<button className="p-1.5 text-[var(--error)] hover:bg-[rgba(232,90,106,0.1)] rounded-[var(--radius-sm)] transition-colors">
|
|
86
91
|
<Trash2 className="w-4 h-4" />
|
|
87
92
|
</button>
|
|
88
93
|
</div>
|
|
@@ -91,29 +96,29 @@ export const WorktreePanel: React.FC<WorktreePanelProps> = ({
|
|
|
91
96
|
))}
|
|
92
97
|
|
|
93
98
|
{worktrees.length === 0 && (
|
|
94
|
-
<div className="col-span-full flex flex-col items-center justify-center py-12 text-muted
|
|
99
|
+
<div className="col-span-full flex flex-col items-center justify-center py-12 text-[var(--text-muted)]">
|
|
95
100
|
<GitBranch className="w-16 h-16 mb-4 opacity-30" />
|
|
96
|
-
<p className="text-
|
|
97
|
-
<p className="text-
|
|
101
|
+
<p className="text-[15px] font-medium text-[var(--text-primary)]">No worktrees yet</p>
|
|
102
|
+
<p className="text-[12px]">Create a worktree to isolate agent changes</p>
|
|
98
103
|
</div>
|
|
99
104
|
)}
|
|
100
105
|
</div>
|
|
101
106
|
</div>
|
|
102
107
|
|
|
103
108
|
{showCreateModal && (
|
|
104
|
-
<div className="fixed inset-0 bg-
|
|
105
|
-
<div className="bg-card border border-border rounded-lg p-6 w-[500px]">
|
|
106
|
-
<h2 className="text-
|
|
109
|
+
<div className="fixed inset-0 bg-[rgba(3,7,9,0.8)] flex items-center justify-center z-50">
|
|
110
|
+
<div className="bg-[var(--bg-card)] border border-[var(--border-subtle)] rounded-[var(--radius-lg)] p-6 w-[500px]">
|
|
111
|
+
<h2 className="text-[16px] font-medium text-[var(--text-primary)] mb-4">Create New Worktree</h2>
|
|
107
112
|
|
|
108
113
|
<div className="space-y-4">
|
|
109
114
|
<div>
|
|
110
|
-
<label className="block text-
|
|
115
|
+
<label className="block text-[11px] font-medium mb-1 text-[var(--text-secondary)]">Repository Path</label>
|
|
111
116
|
<div className="flex gap-2">
|
|
112
117
|
<input
|
|
113
118
|
type="text"
|
|
114
119
|
value={newWorktreeConfig.repoPath}
|
|
115
120
|
onChange={e => setNewWorktreeConfig({ ...newWorktreeConfig, repoPath: e.target.value })}
|
|
116
|
-
className="flex-1 px-3 py-2 bg-
|
|
121
|
+
className="flex-1 px-3 py-2 bg-[var(--bg-elevated)] border border-[var(--border-subtle)] rounded-[var(--radius-md)] text-[13px] text-[var(--text-primary)] placeholder:text-[var(--text-disabled)] focus:outline-none focus:border-[var(--teal-500)]"
|
|
117
122
|
placeholder="/path/to/repo"
|
|
118
123
|
/>
|
|
119
124
|
<button
|
|
@@ -121,7 +126,7 @@ export const WorktreePanel: React.FC<WorktreePanelProps> = ({
|
|
|
121
126
|
const path = await window.electronAPI.dialog.selectFolder();
|
|
122
127
|
if (path) setNewWorktreeConfig({ ...newWorktreeConfig, repoPath: path });
|
|
123
128
|
}}
|
|
124
|
-
className="px-3 py-2 bg-
|
|
129
|
+
className="px-3 py-2 bg-[var(--bg-hover)] rounded-[var(--radius-md)] hover:bg-[var(--bg-active)] text-[13px] text-[var(--text-secondary)] transition-colors"
|
|
125
130
|
>
|
|
126
131
|
Browse
|
|
127
132
|
</button>
|
|
@@ -129,12 +134,12 @@ export const WorktreePanel: React.FC<WorktreePanelProps> = ({
|
|
|
129
134
|
</div>
|
|
130
135
|
|
|
131
136
|
<div>
|
|
132
|
-
<label className="block text-
|
|
137
|
+
<label className="block text-[11px] font-medium mb-1 text-[var(--text-secondary)]">Worktree Name</label>
|
|
133
138
|
<input
|
|
134
139
|
type="text"
|
|
135
140
|
value={newWorktreeConfig.name}
|
|
136
141
|
onChange={e => setNewWorktreeConfig({ ...newWorktreeConfig, name: e.target.value })}
|
|
137
|
-
className="w-full px-3 py-2 bg-
|
|
142
|
+
className="w-full px-3 py-2 bg-[var(--bg-elevated)] border border-[var(--border-subtle)] rounded-[var(--radius-md)] text-[13px] text-[var(--text-primary)] placeholder:text-[var(--text-disabled)] focus:outline-none focus:border-[var(--teal-500)]"
|
|
138
143
|
placeholder="feature-branch"
|
|
139
144
|
/>
|
|
140
145
|
</div>
|
|
@@ -143,14 +148,14 @@ export const WorktreePanel: React.FC<WorktreePanelProps> = ({
|
|
|
143
148
|
<div className="flex justify-end gap-2 mt-6">
|
|
144
149
|
<button
|
|
145
150
|
onClick={() => setShowCreateModal(false)}
|
|
146
|
-
className="px-4 py-2 text-muted-
|
|
151
|
+
className="px-4 py-2 text-[var(--text-muted)] hover:text-[var(--text-primary)] text-[13px] transition-colors"
|
|
147
152
|
>
|
|
148
153
|
Cancel
|
|
149
154
|
</button>
|
|
150
155
|
<button
|
|
151
156
|
onClick={handleCreate}
|
|
152
157
|
disabled={!newWorktreeConfig.repoPath || !newWorktreeConfig.name}
|
|
153
|
-
className="px-4 py-2 bg-
|
|
158
|
+
className="px-4 py-2 bg-[var(--teal-500)] text-[var(--bg-void)] rounded-[var(--radius-sm)] disabled:opacity-50 text-[13px] font-medium transition-colors hover:bg-[var(--teal-400)]"
|
|
154
159
|
>
|
|
155
160
|
Create Worktree
|
|
156
161
|
</button>
|
|
@@ -160,4 +165,4 @@ export const WorktreePanel: React.FC<WorktreePanelProps> = ({
|
|
|
160
165
|
)}
|
|
161
166
|
</div>
|
|
162
167
|
);
|
|
163
|
-
};
|
|
168
|
+
};
|