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
|
@@ -61,22 +61,23 @@ export const SettingsPanel: React.FC<SettingsPanelProps> = ({
|
|
|
61
61
|
|
|
62
62
|
return (
|
|
63
63
|
<div className="h-full flex flex-col">
|
|
64
|
-
<div className="p-4 border-b border-border">
|
|
64
|
+
<div className="p-4 border-b border-[var(--border-subtle)]">
|
|
65
65
|
<div className="flex items-center gap-2 mb-4">
|
|
66
|
-
<SettingsIcon className="w-5 h-5" />
|
|
67
|
-
<h2
|
|
66
|
+
<SettingsIcon className="w-5 h-5 text-[var(--teal-400)]" />
|
|
67
|
+
<h2
|
|
68
|
+
className="text-[18px] font-medium text-[var(--text-primary)]"
|
|
69
|
+
style={{ fontFamily: 'var(--font-display)', fontStyle: 'italic', fontWeight: 300 }}
|
|
70
|
+
>
|
|
71
|
+
Settings
|
|
72
|
+
</h2>
|
|
68
73
|
</div>
|
|
69
74
|
|
|
70
|
-
<div className="
|
|
75
|
+
<div className="tabs">
|
|
71
76
|
{tabs.map(tab => (
|
|
72
77
|
<button
|
|
73
78
|
key={tab.id}
|
|
74
79
|
onClick={() => setActiveTab(tab.id)}
|
|
75
|
-
className={`
|
|
76
|
-
activeTab === tab.id
|
|
77
|
-
? 'bg-primary text-primary-foreground'
|
|
78
|
-
: 'text-muted-foreground hover:bg-muted hover:text-foreground'
|
|
79
|
-
}`}
|
|
80
|
+
className={`tab ${activeTab === tab.id ? 'active' : ''}`}
|
|
80
81
|
>
|
|
81
82
|
{tab.label}
|
|
82
83
|
</button>
|
|
@@ -88,87 +89,87 @@ export const SettingsPanel: React.FC<SettingsPanelProps> = ({
|
|
|
88
89
|
{activeTab === 'general' && (
|
|
89
90
|
<div className="max-w-2xl space-y-6">
|
|
90
91
|
<section>
|
|
91
|
-
<h3 className="text-
|
|
92
|
+
<h3 className="text-[14px] font-medium mb-4 text-[var(--text-primary)]">General Settings</h3>
|
|
92
93
|
|
|
93
94
|
<div className="space-y-4">
|
|
94
|
-
<div className="flex items-center justify-between">
|
|
95
|
+
<div className="flex items-center justify-between p-3 bg-[var(--bg-card)] border border-[var(--border-subtle)] rounded-[var(--radius-md)]">
|
|
95
96
|
<div>
|
|
96
|
-
<label className="block font-medium">Auto Save</label>
|
|
97
|
-
<p className="text-
|
|
97
|
+
<label className="block text-[13px] font-medium text-[var(--text-primary)]">Auto Save</label>
|
|
98
|
+
<p className="text-[11px] text-[var(--text-muted)]">Automatically save changes</p>
|
|
98
99
|
</div>
|
|
99
100
|
<input
|
|
100
101
|
type="checkbox"
|
|
101
102
|
checked={localSettings.autoSave}
|
|
102
103
|
onChange={e => setLocalSettings({ ...localSettings, autoSave: e.target.checked })}
|
|
103
|
-
className="w-5 h-5"
|
|
104
|
+
className="w-5 h-5 accent-[var(--teal-500)]"
|
|
104
105
|
/>
|
|
105
106
|
</div>
|
|
106
107
|
|
|
107
|
-
<div className="flex items-center justify-between">
|
|
108
|
+
<div className="flex items-center justify-between p-3 bg-[var(--bg-card)] border border-[var(--border-subtle)] rounded-[var(--radius-md)]">
|
|
108
109
|
<div>
|
|
109
|
-
<label className="block font-medium">Max Parallel Agents</label>
|
|
110
|
-
<p className="text-
|
|
110
|
+
<label className="block text-[13px] font-medium text-[var(--text-primary)]">Max Parallel Agents</label>
|
|
111
|
+
<p className="text-[11px] text-[var(--text-muted)]">Maximum number of agents running simultaneously</p>
|
|
111
112
|
</div>
|
|
112
113
|
<input
|
|
113
114
|
type="number"
|
|
114
115
|
value={localSettings.maxParallelAgents}
|
|
115
116
|
onChange={e => setLocalSettings({ ...localSettings, maxParallelAgents: parseInt(e.target.value) })}
|
|
116
|
-
className="w-20 px-3 py-2 bg-
|
|
117
|
+
className="w-20 px-3 py-2 bg-[var(--bg-elevated)] border border-[var(--border-subtle)] rounded-[var(--radius-md)] text-[13px] text-[var(--text-primary)]"
|
|
117
118
|
min={1}
|
|
118
119
|
max={10}
|
|
119
120
|
/>
|
|
120
121
|
</div>
|
|
121
122
|
|
|
122
|
-
<div className="flex items-center justify-between">
|
|
123
|
+
<div className="flex items-center justify-between p-3 bg-[var(--bg-card)] border border-[var(--border-subtle)] rounded-[var(--radius-md)]">
|
|
123
124
|
<div>
|
|
124
|
-
<label className="block font-medium">Show Notifications</label>
|
|
125
|
-
<p className="text-
|
|
125
|
+
<label className="block text-[13px] font-medium text-[var(--text-primary)]">Show Notifications</label>
|
|
126
|
+
<p className="text-[11px] text-[var(--text-muted)]">Display desktop notifications for agent events</p>
|
|
126
127
|
</div>
|
|
127
128
|
<input
|
|
128
129
|
type="checkbox"
|
|
129
130
|
checked={localSettings.showNotifications}
|
|
130
131
|
onChange={e => setLocalSettings({ ...localSettings, showNotifications: e.target.checked })}
|
|
131
|
-
className="w-5 h-5"
|
|
132
|
+
className="w-5 h-5 accent-[var(--teal-500)]"
|
|
132
133
|
/>
|
|
133
134
|
</div>
|
|
134
135
|
|
|
135
|
-
<div className="flex items-center justify-between">
|
|
136
|
+
<div className="flex items-center justify-between p-3 bg-[var(--bg-card)] border border-[var(--border-subtle)] rounded-[var(--radius-md)]">
|
|
136
137
|
<div>
|
|
137
|
-
<label className="block font-medium">Confirm Destructive Actions</label>
|
|
138
|
-
<p className="text-
|
|
138
|
+
<label className="block text-[13px] font-medium text-[var(--text-primary)]">Confirm Destructive Actions</label>
|
|
139
|
+
<p className="text-[11px] text-[var(--text-muted)]">Show confirmation dialogs before deleting agents or worktrees</p>
|
|
139
140
|
</div>
|
|
140
141
|
<input
|
|
141
142
|
type="checkbox"
|
|
142
143
|
checked={localSettings.confirmDestructiveActions}
|
|
143
144
|
onChange={e => setLocalSettings({ ...localSettings, confirmDestructiveActions: e.target.checked })}
|
|
144
|
-
className="w-5 h-5"
|
|
145
|
+
className="w-5 h-5 accent-[var(--teal-500)]"
|
|
145
146
|
/>
|
|
146
147
|
</div>
|
|
147
148
|
</div>
|
|
148
149
|
</section>
|
|
149
150
|
|
|
150
151
|
<section>
|
|
151
|
-
<h3 className="text-
|
|
152
|
+
<h3 className="text-[14px] font-medium mb-4 text-[var(--text-primary)]">Git Configuration</h3>
|
|
152
153
|
|
|
153
154
|
<div className="space-y-4">
|
|
154
|
-
<div>
|
|
155
|
-
<label className="block font-medium mb-1">Author Name</label>
|
|
155
|
+
<div className="p-4 bg-[var(--bg-card)] border border-[var(--border-subtle)] rounded-[var(--radius-md)]">
|
|
156
|
+
<label className="block text-[12px] font-medium mb-1 text-[var(--text-secondary)]">Author Name</label>
|
|
156
157
|
<input
|
|
157
158
|
type="text"
|
|
158
159
|
value={localSettings.gitAuthorName}
|
|
159
160
|
onChange={e => setLocalSettings({ ...localSettings, gitAuthorName: e.target.value })}
|
|
160
|
-
className="w-full px-3 py-2 bg-
|
|
161
|
+
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)]"
|
|
161
162
|
placeholder="Your Name"
|
|
162
163
|
/>
|
|
163
164
|
</div>
|
|
164
165
|
|
|
165
|
-
<div>
|
|
166
|
-
<label className="block font-medium mb-1">Author Email</label>
|
|
166
|
+
<div className="p-4 bg-[var(--bg-card)] border border-[var(--border-subtle)] rounded-[var(--radius-md)]">
|
|
167
|
+
<label className="block text-[12px] font-medium mb-1 text-[var(--text-secondary)]">Author Email</label>
|
|
167
168
|
<input
|
|
168
169
|
type="email"
|
|
169
170
|
value={localSettings.gitAuthorEmail}
|
|
170
171
|
onChange={e => setLocalSettings({ ...localSettings, gitAuthorEmail: e.target.value })}
|
|
171
|
-
className="w-full px-3 py-2 bg-
|
|
172
|
+
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)]"
|
|
172
173
|
placeholder="your@email.com"
|
|
173
174
|
/>
|
|
174
175
|
</div>
|
|
@@ -180,21 +181,21 @@ export const SettingsPanel: React.FC<SettingsPanelProps> = ({
|
|
|
180
181
|
{activeTab === 'providers' && (
|
|
181
182
|
<div className="max-w-4xl space-y-6">
|
|
182
183
|
{freeModelsProvider && (
|
|
183
|
-
<section className="p-4 border-2 border-
|
|
184
|
+
<section className="p-4 border-2 border-[rgba(60,200,120,0.3)] bg-[rgba(60,200,120,0.05)] rounded-[var(--radius-lg)]">
|
|
184
185
|
<div className="flex items-center gap-2 mb-4">
|
|
185
|
-
<Sparkles className="w-5 h-5 text-
|
|
186
|
-
<h3 className="text-
|
|
187
|
-
<span className="
|
|
186
|
+
<Sparkles className="w-5 h-5 text-[var(--success)]" />
|
|
187
|
+
<h3 className="text-[15px] font-medium text-[var(--text-primary)]">Free AI Models</h3>
|
|
188
|
+
<span className="badge badge-success">
|
|
188
189
|
No API Key Required
|
|
189
190
|
</span>
|
|
190
191
|
</div>
|
|
191
|
-
<p className="text-
|
|
192
|
+
<p className="text-[12px] text-[var(--text-muted)] mb-4">
|
|
192
193
|
Start using AI immediately with free models from OpenRouter, Groq, Google AI Studio, and Cerebras.
|
|
193
194
|
No API key required for OpenRouter free tier - just select a model and start chatting!
|
|
194
195
|
</p>
|
|
195
196
|
|
|
196
197
|
<div className="mb-4">
|
|
197
|
-
<label className="block text-
|
|
198
|
+
<label className="block text-[12px] font-medium mb-2 text-[var(--text-secondary)]">Select Model</label>
|
|
198
199
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-2 max-h-96 overflow-y-auto">
|
|
199
200
|
{freeModelsProvider.models
|
|
200
201
|
.sort((a, b) => {
|
|
@@ -207,59 +208,54 @@ export const SettingsPanel: React.FC<SettingsPanelProps> = ({
|
|
|
207
208
|
<button
|
|
208
209
|
key={model.id}
|
|
209
210
|
onClick={() => handleModelSelect(model.id, freeModelsProvider.id)}
|
|
210
|
-
className={`p-3 text-left rounded-
|
|
211
|
+
className={`p-3 text-left rounded-[var(--radius-md)] border transition-all ${
|
|
211
212
|
selectedModel === model.id
|
|
212
|
-
? 'border-
|
|
213
|
-
: 'border-border hover:border-
|
|
213
|
+
? 'border-[var(--teal-500)] bg-[rgba(0,200,168,0.08)]'
|
|
214
|
+
: 'border-[var(--border-subtle)] hover:border-[var(--teal-500)] hover:bg-[var(--bg-hover)]'
|
|
214
215
|
}`}
|
|
215
216
|
>
|
|
216
217
|
<div className="flex items-center justify-between">
|
|
217
|
-
<span className="font-medium text-
|
|
218
|
+
<span className="font-medium text-[12px] truncate text-[var(--text-primary)]">{model.name}</span>
|
|
218
219
|
{selectedModel === model.id && (
|
|
219
|
-
<Check className="w-4 h-4 text-
|
|
220
|
+
<Check className="w-4 h-4 text-[var(--teal-400)] flex-shrink-0" />
|
|
220
221
|
)}
|
|
221
222
|
</div>
|
|
222
223
|
<div className="flex items-center gap-1 mt-1">
|
|
223
|
-
<span className="text-
|
|
224
|
+
<span className="text-[10px] px-1.5 py-0.5 rounded bg-[rgba(104,144,244,0.1)] text-[var(--info)]">
|
|
224
225
|
{model.backend || 'openrouter'}
|
|
225
226
|
</span>
|
|
226
227
|
{model.supportsVision && (
|
|
227
|
-
<span className="text-
|
|
228
|
+
<span className="text-[10px] px-1.5 py-0.5 rounded bg-[rgba(136,88,219,0.1)] text-purple-400">
|
|
228
229
|
vision
|
|
229
230
|
</span>
|
|
230
231
|
)}
|
|
231
232
|
{model.contextWindow >= 100000 && (
|
|
232
|
-
<span className="text-
|
|
233
|
+
<span className="text-[10px] px-1.5 py-0.5 rounded bg-[rgba(230,185,74,0.1)] text-[var(--warning)]">
|
|
233
234
|
{model.contextWindow >= 1000000 ? '1M ctx' : `${model.contextWindow / 1000}k ctx`}
|
|
234
235
|
</span>
|
|
235
236
|
)}
|
|
236
237
|
</div>
|
|
237
|
-
<p className="text-
|
|
238
|
+
<p className="text-[10px] text-[var(--text-muted)] mt-1 truncate">{model.description}</p>
|
|
238
239
|
</button>
|
|
239
240
|
))}
|
|
240
241
|
</div>
|
|
241
242
|
</div>
|
|
242
|
-
|
|
243
|
-
<div className="text-xs text-muted-foreground">
|
|
244
|
-
<strong>Tip:</strong> OpenRouter models work without an API key. For Groq, Google AI Studio, or Cerebras models,
|
|
245
|
-
you may need to add your free API key below.
|
|
246
|
-
</div>
|
|
247
243
|
</section>
|
|
248
244
|
)}
|
|
249
245
|
|
|
250
246
|
<section>
|
|
251
|
-
<h3 className="text-
|
|
252
|
-
<p className="text-
|
|
247
|
+
<h3 className="text-[15px] font-medium mb-4 text-[var(--text-primary)]">Premium Providers</h3>
|
|
248
|
+
<p className="text-[12px] text-[var(--text-muted)] mb-4">
|
|
253
249
|
Add your API keys for premium models with higher limits and advanced features.
|
|
254
250
|
</p>
|
|
255
251
|
|
|
256
252
|
<div className="space-y-4">
|
|
257
253
|
{paidProviders.map(provider => (
|
|
258
|
-
<div key={provider.id} className="p-4 bg-
|
|
254
|
+
<div key={provider.id} className="p-4 bg-[var(--bg-card)] border border-[var(--border-subtle)] rounded-[var(--radius-lg)]">
|
|
259
255
|
<div className="flex items-center justify-between mb-3">
|
|
260
256
|
<div>
|
|
261
|
-
<h4 className="font-medium">{provider.name}</h4>
|
|
262
|
-
<p className="text-
|
|
257
|
+
<h4 className="font-medium text-[13px] text-[var(--text-primary)]">{provider.name}</h4>
|
|
258
|
+
<p className="text-[11px] text-[var(--text-muted)]">{provider.description}</p>
|
|
263
259
|
</div>
|
|
264
260
|
<div className="flex items-center gap-2">
|
|
265
261
|
<input
|
|
@@ -267,21 +263,21 @@ export const SettingsPanel: React.FC<SettingsPanelProps> = ({
|
|
|
267
263
|
name="activeProvider"
|
|
268
264
|
checked={localSettings.defaultProvider === provider.id}
|
|
269
265
|
onChange={() => setLocalSettings({ ...localSettings, defaultProvider: provider.id })}
|
|
270
|
-
className="w-4 h-4"
|
|
266
|
+
className="w-4 h-4 accent-[var(--teal-500)]"
|
|
271
267
|
/>
|
|
272
|
-
<span className="text-
|
|
268
|
+
<span className="text-[12px] text-[var(--text-secondary)]">Default</span>
|
|
273
269
|
</div>
|
|
274
270
|
</div>
|
|
275
271
|
|
|
276
272
|
<div>
|
|
277
|
-
<label className="block text-
|
|
273
|
+
<label className="block text-[12px] font-medium mb-1 text-[var(--text-secondary)]">API Key</label>
|
|
278
274
|
<div className="flex gap-2">
|
|
279
275
|
<div className="relative flex-1">
|
|
280
276
|
<input
|
|
281
277
|
type={showApiKeys[provider.id] ? 'text' : 'password'}
|
|
282
278
|
defaultValue={provider.config.apiKey}
|
|
283
279
|
placeholder={`Enter ${provider.name} API key`}
|
|
284
|
-
className="w-full px-3 py-2 bg-
|
|
280
|
+
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)] pr-10 focus:outline-none focus:border-[var(--teal-500)]"
|
|
285
281
|
onBlur={e => handleProviderConfig(provider.id, e.target.value)}
|
|
286
282
|
/>
|
|
287
283
|
<button
|
|
@@ -289,14 +285,14 @@ export const SettingsPanel: React.FC<SettingsPanelProps> = ({
|
|
|
289
285
|
...showApiKeys,
|
|
290
286
|
[provider.id]: !showApiKeys[provider.id]
|
|
291
287
|
})}
|
|
292
|
-
className="absolute right-2 top-1/2 -translate-y-1/2 text-muted
|
|
288
|
+
className="absolute right-2 top-1/2 -translate-y-1/2 text-[var(--text-muted)] hover:text-[var(--text-secondary)]"
|
|
293
289
|
>
|
|
294
290
|
{showApiKeys[provider.id] ? <EyeOff className="w-4 h-4" /> : <Eye className="w-4 h-4" />}
|
|
295
291
|
</button>
|
|
296
292
|
</div>
|
|
297
293
|
<button
|
|
298
294
|
onClick={() => handleProviderConfig(provider.id, provider.config.apiKey || '')}
|
|
299
|
-
className="px-3 py-2 bg-
|
|
295
|
+
className="px-3 py-2 bg-[var(--teal-500)] text-[var(--bg-void)] rounded-[var(--radius-md)] hover:bg-[var(--teal-400)] text-[13px] font-medium transition-colors"
|
|
300
296
|
>
|
|
301
297
|
Test
|
|
302
298
|
</button>
|
|
@@ -305,12 +301,12 @@ export const SettingsPanel: React.FC<SettingsPanelProps> = ({
|
|
|
305
301
|
|
|
306
302
|
{provider.models.length > 0 && (
|
|
307
303
|
<div className="mt-3">
|
|
308
|
-
<label className="block text-
|
|
304
|
+
<label className="block text-[12px] font-medium mb-1 text-[var(--text-secondary)]">Available Models</label>
|
|
309
305
|
<div className="flex flex-wrap gap-2">
|
|
310
306
|
{provider.models.map(model => (
|
|
311
307
|
<span
|
|
312
308
|
key={model.id}
|
|
313
|
-
className="px-2 py-1 bg-
|
|
309
|
+
className="px-2 py-1 bg-[var(--bg-hover)] rounded text-[11px] text-[var(--text-secondary)]"
|
|
314
310
|
>
|
|
315
311
|
{model.name}
|
|
316
312
|
</span>
|
|
@@ -323,18 +319,16 @@ export const SettingsPanel: React.FC<SettingsPanelProps> = ({
|
|
|
323
319
|
</div>
|
|
324
320
|
</section>
|
|
325
321
|
|
|
326
|
-
<section className="p-4 bg-
|
|
322
|
+
<section className="p-4 bg-[rgba(104,144,244,0.1)] border border-[rgba(104,144,244,0.3)] rounded-[var(--radius-lg)]">
|
|
327
323
|
<div className="flex items-center gap-2 mb-2">
|
|
328
|
-
<Zap className="w-4 h-4 text-
|
|
329
|
-
<h4 className="font-medium text-
|
|
324
|
+
<Zap className="w-4 h-4 text-[var(--info)]" />
|
|
325
|
+
<h4 className="font-medium text-[var(--info)]">Get Free API Keys</h4>
|
|
330
326
|
</div>
|
|
331
|
-
<div className="text-
|
|
332
|
-
<p><strong>OpenRouter:</strong> <a href="https://openrouter.ai" target="_blank" rel="noopener noreferrer" className="text-
|
|
333
|
-
<p><strong>Ollama:</strong> <a href="https://ollama.com" target="_blank" rel="noopener noreferrer" className="text-
|
|
334
|
-
<p><strong>NVIDIA NIM:</strong> <a href="https://build.nvidia.com" target="_blank" rel="noopener noreferrer" className="text-
|
|
335
|
-
<p><strong>Groq:</strong> <a href="https://console.groq.com" target="_blank" rel="noopener noreferrer" className="text-
|
|
336
|
-
<p><strong>Google AI Studio:</strong> <a href="https://aistudio.google.com" target="_blank" rel="noopener noreferrer" className="text-blue-400 hover:underline">aistudio.google.com</a> - Gemini models free tier</p>
|
|
337
|
-
<p><strong>Cerebras:</strong> <a href="https://cloud.cerebras.ai" target="_blank" rel="noopener noreferrer" className="text-blue-400 hover:underline">cloud.cerebras.ai</a> - Ultra-fast inference</p>
|
|
327
|
+
<div className="text-[12px] text-[var(--text-muted)] space-y-1">
|
|
328
|
+
<p><strong>OpenRouter:</strong> <a href="https://openrouter.ai" target="_blank" rel="noopener noreferrer" className="text-[var(--teal-400)] hover:underline">openrouter.ai</a> - No key needed for free models</p>
|
|
329
|
+
<p><strong>Ollama:</strong> <a href="https://ollama.com" target="_blank" rel="noopener noreferrer" className="text-[var(--teal-400)] hover:underline">ollama.com</a> - Local models, no API key needed</p>
|
|
330
|
+
<p><strong>NVIDIA NIM:</strong> <a href="https://build.nvidia.com" target="_blank" rel="noopener noreferrer" className="text-[var(--teal-400)] hover:underline">build.nvidia.com</a> - Kimi K2.5, DeepSeek R1, Llama (40 req/min free)</p>
|
|
331
|
+
<p><strong>Groq:</strong> <a href="https://console.groq.com" target="_blank" rel="noopener noreferrer" className="text-[var(--teal-400)] hover:underline">console.groq.com</a> - Fast inference, generous free tier</p>
|
|
338
332
|
</div>
|
|
339
333
|
</section>
|
|
340
334
|
</div>
|
|
@@ -343,42 +337,41 @@ export const SettingsPanel: React.FC<SettingsPanelProps> = ({
|
|
|
343
337
|
{activeTab === 'appearance' && (
|
|
344
338
|
<div className="max-w-2xl space-y-6">
|
|
345
339
|
<section>
|
|
346
|
-
<h3 className="text-
|
|
340
|
+
<h3 className="text-[14px] font-medium mb-4 text-[var(--text-primary)]">Appearance</h3>
|
|
347
341
|
|
|
348
342
|
<div className="space-y-4">
|
|
349
|
-
<div>
|
|
350
|
-
<label className="block font-medium mb-1">Theme</label>
|
|
343
|
+
<div className="p-4 bg-[var(--bg-card)] border border-[var(--border-subtle)] rounded-[var(--radius-md)]">
|
|
344
|
+
<label className="block text-[12px] font-medium mb-1 text-[var(--text-secondary)]">Theme</label>
|
|
351
345
|
<select
|
|
352
346
|
value={localSettings.theme}
|
|
353
347
|
onChange={e => setLocalSettings({ ...localSettings, theme: e.target.value as any })}
|
|
354
|
-
className="w-full px-3 py-2 bg-
|
|
348
|
+
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)]"
|
|
355
349
|
>
|
|
356
|
-
<option value="
|
|
357
|
-
<option value="dark">Dark</option>
|
|
350
|
+
<option value="dark">Dark (Abyss Teal)</option>
|
|
358
351
|
<option value="system">System</option>
|
|
359
352
|
</select>
|
|
360
353
|
</div>
|
|
361
354
|
|
|
362
|
-
<div>
|
|
363
|
-
<label className="block font-medium mb-1">Font Size</label>
|
|
355
|
+
<div className="p-4 bg-[var(--bg-card)] border border-[var(--border-subtle)] rounded-[var(--radius-md)]">
|
|
356
|
+
<label className="block text-[12px] font-medium mb-1 text-[var(--text-secondary)]">Font Size</label>
|
|
364
357
|
<input
|
|
365
358
|
type="range"
|
|
366
359
|
value={localSettings.fontSize}
|
|
367
360
|
onChange={e => setLocalSettings({ ...localSettings, fontSize: parseInt(e.target.value) })}
|
|
368
|
-
className="w-full"
|
|
361
|
+
className="w-full accent-[var(--teal-500)]"
|
|
369
362
|
min={10}
|
|
370
363
|
max={20}
|
|
371
364
|
/>
|
|
372
|
-
<span className="text-
|
|
365
|
+
<span className="text-[12px] text-[var(--text-muted)]">{localSettings.fontSize}px</span>
|
|
373
366
|
</div>
|
|
374
367
|
|
|
375
|
-
<div>
|
|
376
|
-
<label className="block font-medium mb-1">Font Family</label>
|
|
368
|
+
<div className="p-4 bg-[var(--bg-card)] border border-[var(--border-subtle)] rounded-[var(--radius-md)]">
|
|
369
|
+
<label className="block text-[12px] font-medium mb-1 text-[var(--text-secondary)]">Font Family</label>
|
|
377
370
|
<input
|
|
378
371
|
type="text"
|
|
379
372
|
value={localSettings.fontFamily}
|
|
380
373
|
onChange={e => setLocalSettings({ ...localSettings, fontFamily: e.target.value })}
|
|
381
|
-
className="w-full px-3 py-2 bg-
|
|
374
|
+
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)] focus:outline-none focus:border-[var(--teal-500)]"
|
|
382
375
|
/>
|
|
383
376
|
</div>
|
|
384
377
|
</div>
|
|
@@ -389,12 +382,12 @@ export const SettingsPanel: React.FC<SettingsPanelProps> = ({
|
|
|
389
382
|
{activeTab === 'shortcuts' && (
|
|
390
383
|
<div className="max-w-2xl">
|
|
391
384
|
<section>
|
|
392
|
-
<h3 className="text-
|
|
385
|
+
<h3 className="text-[14px] font-medium mb-4 text-[var(--text-primary)]">Keyboard Shortcuts</h3>
|
|
393
386
|
|
|
394
|
-
<div className="space-y-
|
|
387
|
+
<div className="space-y-2">
|
|
395
388
|
{Object.entries(localSettings.shortcuts).map(([action, shortcut]) => (
|
|
396
|
-
<div key={action} className="flex items-center justify-between py-2
|
|
397
|
-
<span className="text-
|
|
389
|
+
<div key={action} className="flex items-center justify-between py-2 px-3 bg-[var(--bg-card)] border border-[var(--border-subtle)] rounded-[var(--radius-md)]">
|
|
390
|
+
<span className="text-[12px] capitalize text-[var(--text-secondary)]">{action.replace(/:/g, ' ')}</span>
|
|
398
391
|
<input
|
|
399
392
|
type="text"
|
|
400
393
|
value={shortcut}
|
|
@@ -402,7 +395,7 @@ export const SettingsPanel: React.FC<SettingsPanelProps> = ({
|
|
|
402
395
|
...localSettings,
|
|
403
396
|
shortcuts: { ...localSettings.shortcuts, [action]: e.target.value }
|
|
404
397
|
})}
|
|
405
|
-
className="px-3 py-1 bg-
|
|
398
|
+
className="px-3 py-1 bg-[var(--bg-elevated)] border border-[var(--border-subtle)] rounded-[var(--radius-sm)] text-[11px] font-[var(--font-mono)] text-[var(--teal-300)] w-32 focus:outline-none focus:border-[var(--teal-500)]"
|
|
406
399
|
/>
|
|
407
400
|
</div>
|
|
408
401
|
))}
|
|
@@ -412,10 +405,10 @@ export const SettingsPanel: React.FC<SettingsPanelProps> = ({
|
|
|
412
405
|
)}
|
|
413
406
|
</div>
|
|
414
407
|
|
|
415
|
-
<div className="p-4 border-t border-border flex justify-end">
|
|
408
|
+
<div className="p-4 border-t border-[var(--border-subtle)] flex justify-end bg-[var(--bg-surface)]">
|
|
416
409
|
<button
|
|
417
410
|
onClick={handleSave}
|
|
418
|
-
className="flex items-center gap-2 px-4 py-2 bg-
|
|
411
|
+
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"
|
|
419
412
|
>
|
|
420
413
|
<Save className="w-4 h-4" />
|
|
421
414
|
Save Settings
|
|
@@ -5,11 +5,10 @@ import {
|
|
|
5
5
|
Wrench,
|
|
6
6
|
Clock,
|
|
7
7
|
Settings,
|
|
8
|
-
ChevronRight,
|
|
9
8
|
Code2,
|
|
10
|
-
ScrollText
|
|
9
|
+
ScrollText,
|
|
10
|
+
MessageSquare
|
|
11
11
|
} from 'lucide-react';
|
|
12
|
-
import { cn } from '@/lib/utils';
|
|
13
12
|
|
|
14
13
|
interface SidebarProps {
|
|
15
14
|
activeTab: string;
|
|
@@ -17,32 +16,31 @@ interface SidebarProps {
|
|
|
17
16
|
}
|
|
18
17
|
|
|
19
18
|
const menuItems = [
|
|
20
|
-
{ id: '
|
|
21
|
-
{ id: '
|
|
22
|
-
{ id: '
|
|
23
|
-
{ id: '
|
|
24
|
-
{ id: '
|
|
25
|
-
{ id: '
|
|
19
|
+
{ id: 'chat', label: 'Chat', icon: MessageSquare },
|
|
20
|
+
{ id: 'agents', label: 'Agents', icon: Bot },
|
|
21
|
+
{ id: 'code', label: 'Code', icon: Code2 },
|
|
22
|
+
{ id: 'worktrees', label: 'Worktrees', icon: GitBranch },
|
|
23
|
+
{ id: 'skills', label: 'Skills', icon: Wrench },
|
|
24
|
+
{ id: 'automations', label: 'Automations', icon: Clock },
|
|
25
|
+
{ id: 'audit', label: 'Audit', icon: ScrollText },
|
|
26
26
|
];
|
|
27
27
|
|
|
28
28
|
export const Sidebar: React.FC<SidebarProps> = ({ activeTab, onTabChange }) => {
|
|
29
29
|
return (
|
|
30
|
-
<aside
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
<span className="font-semibold text-lg tracking-tight">Codex</span>
|
|
39
|
-
<span className="text-xs text-neutral-400 block">Linux</span>
|
|
30
|
+
<aside
|
|
31
|
+
className="w-[200px] bg-[var(--bg-surface)] border-r border-[var(--border-subtle)] flex flex-col"
|
|
32
|
+
data-testid="sidebar"
|
|
33
|
+
>
|
|
34
|
+
<div className="p-4 border-b border-[var(--border-faint)]">
|
|
35
|
+
<div className="flex items-center gap-2">
|
|
36
|
+
<div className="w-[22px] h-[22px] bg-[var(--teal-500)] rounded-[6px] flex items-center justify-center">
|
|
37
|
+
<span className="text-[12px] font-bold text-[var(--bg-void)] font-[var(--font-body)]">C</span>
|
|
40
38
|
</div>
|
|
39
|
+
<span className="font-medium text-[14px] text-[var(--text-primary)]">Codex</span>
|
|
41
40
|
</div>
|
|
42
41
|
</div>
|
|
43
42
|
|
|
44
|
-
|
|
45
|
-
<nav className="flex-1 p-3 space-y-1 overflow-y-auto" data-testid="sidebar-nav">
|
|
43
|
+
<nav className="flex-1 p-3 space-y-0.5 overflow-y-auto" data-testid="sidebar-nav">
|
|
46
44
|
{menuItems.map((item) => {
|
|
47
45
|
const Icon = item.icon;
|
|
48
46
|
const isActive = activeTab === item.id;
|
|
@@ -51,50 +49,34 @@ export const Sidebar: React.FC<SidebarProps> = ({ activeTab, onTabChange }) => {
|
|
|
51
49
|
<button
|
|
52
50
|
key={item.id}
|
|
53
51
|
onClick={() => onTabChange(item.id)}
|
|
54
|
-
className={
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
)}
|
|
52
|
+
className={`w-full flex items-center gap-2 px-2.5 py-2 rounded-[var(--radius-sm)] text-[12px] transition-all duration-[150ms] ${
|
|
53
|
+
isActive
|
|
54
|
+
? 'bg-[rgba(0,200,168,0.08)] text-[var(--teal-300)] border-l-[2px] border-[var(--teal-500)] pl-[9px]'
|
|
55
|
+
: 'text-[var(--text-muted)] hover:bg-[var(--bg-hover)] hover:text-[var(--text-secondary)]'
|
|
56
|
+
}`}
|
|
60
57
|
data-testid={`nav-${item.id}`}
|
|
61
58
|
>
|
|
62
|
-
<Icon className=
|
|
63
|
-
|
|
64
|
-
isActive ? '' : 'group-hover:scale-110'
|
|
65
|
-
)} />
|
|
66
|
-
<div className="flex-1 text-left">
|
|
67
|
-
<span className="block">{item.label}</span>
|
|
68
|
-
{!isActive && (
|
|
69
|
-
<span className="text-xs text-neutral-400 font-normal opacity-0 group-hover:opacity-100 transition-opacity">
|
|
70
|
-
{item.description}
|
|
71
|
-
</span>
|
|
72
|
-
)}
|
|
73
|
-
</div>
|
|
74
|
-
{isActive && (
|
|
75
|
-
<ChevronRight className="w-4 h-4 opacity-50" />
|
|
76
|
-
)}
|
|
59
|
+
<Icon className="w-3.5 h-3.5 opacity-50" />
|
|
60
|
+
<span>{item.label}</span>
|
|
77
61
|
</button>
|
|
78
62
|
);
|
|
79
63
|
})}
|
|
80
64
|
</nav>
|
|
81
65
|
|
|
82
|
-
|
|
83
|
-
<div className="p-3 border-t border-[var(--color-border)]">
|
|
66
|
+
<div className="p-3 border-t border-[var(--border-faint)]">
|
|
84
67
|
<button
|
|
85
68
|
onClick={() => onTabChange('settings')}
|
|
86
|
-
className={
|
|
87
|
-
'w-full flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium transition-all duration-200',
|
|
69
|
+
className={`w-full flex items-center gap-2 px-2.5 py-2 rounded-[var(--radius-sm)] text-[12px] transition-all duration-[150ms] ${
|
|
88
70
|
activeTab === 'settings'
|
|
89
|
-
? 'bg-
|
|
90
|
-
: 'text-
|
|
91
|
-
|
|
71
|
+
? 'bg-[rgba(0,200,168,0.08)] text-[var(--teal-300)] border-l-[2px] border-[var(--teal-500)] pl-[9px]'
|
|
72
|
+
: 'text-[var(--text-muted)] hover:bg-[var(--bg-hover)] hover:text-[var(--text-secondary)]'
|
|
73
|
+
}`}
|
|
92
74
|
data-testid="nav-settings"
|
|
93
75
|
>
|
|
94
|
-
<Settings className="w-
|
|
76
|
+
<Settings className="w-3.5 h-3.5 opacity-50" />
|
|
95
77
|
<span>Settings</span>
|
|
96
78
|
</button>
|
|
97
79
|
</div>
|
|
98
80
|
</aside>
|
|
99
81
|
);
|
|
100
|
-
};
|
|
82
|
+
};
|