Skip to content

Commit 8debc24

Browse files
luokerenx4claude
andcommitted
ui: unified button classes, HeartbeatPage/ToolsPage layout polish
Add btn-primary/secondary/danger CSS utility classes with focus-visible rings. Replace 10+ inline button styles across pages and components. HeartbeatPage uses ConfigSection two-column layout. Both pages centered at 880px. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent e912056 commit 8debc24

10 files changed

Lines changed: 65 additions & 28 deletions

ui/src/components/ChannelConfigModal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ export function ChannelConfigModal({ channel, onClose, onSaved }: ChannelConfigM
321321
<button
322322
onClick={handleSave}
323323
disabled={saving}
324-
className="text-sm px-4 py-1.5 rounded-lg bg-accent text-white hover:bg-accent/80 transition-colors disabled:opacity-50"
324+
className="btn-primary"
325325
>
326326
{saving ? 'Saving...' : 'Save'}
327327
</button>

ui/src/components/PushApprovalPanel.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ export function PushApprovalPanel() {
258258
<button
259259
onClick={() => handlePush(account.id)}
260260
disabled={pushing !== null}
261-
className="px-2 py-1 rounded font-medium bg-accent text-white hover:bg-accent/80 disabled:opacity-50 transition-colors"
261+
className="btn-primary-sm"
262262
>
263263
{pushing === account.id ? '...' : 'Confirm'}
264264
</button>
@@ -274,7 +274,7 @@ export function PushApprovalPanel() {
274274
<button
275275
onClick={() => setConfirmingPush(account.id)}
276276
disabled={pushing !== null || rejecting !== null}
277-
className="flex-1 text-xs px-3 py-1.5 rounded font-medium bg-accent text-white hover:bg-accent/80 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
277+
className="flex-1 btn-primary-sm"
278278
>
279279
Approve & Push
280280
</button>

ui/src/index.css

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,43 @@ body,
3131
height: 100%;
3232
}
3333

34+
/* ==================== Button utility classes ==================== */
35+
36+
.btn-primary {
37+
@apply px-4 py-2 text-[13px] font-medium rounded-lg bg-accent text-white
38+
hover:bg-accent/85 transition-colors
39+
disabled:opacity-40 disabled:cursor-default cursor-pointer
40+
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/40 focus-visible:ring-offset-2 focus-visible:ring-offset-bg;
41+
}
42+
43+
.btn-secondary {
44+
@apply px-4 py-2 text-[13px] font-medium rounded-lg border border-border text-text-muted
45+
hover:bg-bg-tertiary hover:text-text transition-colors
46+
disabled:opacity-40 disabled:cursor-default cursor-pointer
47+
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/40 focus-visible:ring-offset-2 focus-visible:ring-offset-bg;
48+
}
49+
50+
.btn-danger {
51+
@apply px-4 py-2 text-[13px] font-medium rounded-lg border border-red/30 text-red
52+
hover:bg-red/10 transition-colors
53+
disabled:opacity-40 disabled:cursor-default cursor-pointer
54+
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-red/40 focus-visible:ring-offset-2 focus-visible:ring-offset-bg;
55+
}
56+
57+
.btn-primary-sm {
58+
@apply px-3 py-1.5 text-xs font-medium rounded-md bg-accent text-white
59+
hover:bg-accent/85 transition-colors
60+
disabled:opacity-40 disabled:cursor-default cursor-pointer
61+
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/40 focus-visible:ring-offset-1 focus-visible:ring-offset-bg;
62+
}
63+
64+
.btn-secondary-sm {
65+
@apply px-3 py-1.5 text-xs font-medium rounded-md border border-border text-text-muted
66+
hover:bg-bg-tertiary hover:text-text transition-colors
67+
disabled:opacity-40 disabled:cursor-default cursor-pointer
68+
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/40 focus-visible:ring-offset-1 focus-visible:ring-offset-bg;
69+
}
70+
3471
/* Scrollbar styling */
3572
::-webkit-scrollbar {
3673
width: 6px;

ui/src/pages/AIProviderPage.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,7 @@ function ModelForm({ aiProvider }: { aiProvider: AIProviderConfig }) {
382382
<button
383383
onClick={handleSaveKeys}
384384
disabled={keySaveStatus === 'saving'}
385-
className="bg-user-bubble text-white rounded-lg px-4 py-2 text-[13px] font-medium cursor-pointer transition-opacity hover:opacity-85 disabled:opacity-50"
385+
className="btn-primary"
386386
>
387387
Save Keys
388388
</button>
@@ -465,7 +465,7 @@ function AgentSdkAuthForm({ aiProvider, onUpdate }: { aiProvider: AIProviderConf
465465
<button
466466
onClick={handleSaveKey}
467467
disabled={!apiKey || keySaveStatus === 'saving'}
468-
className="bg-user-bubble text-white rounded-lg px-4 py-2 text-[13px] font-medium cursor-pointer transition-opacity hover:opacity-85 disabled:opacity-50"
468+
className="btn-primary"
469469
>
470470
Save Key
471471
</button>

ui/src/pages/DevPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ function SendSection() {
184184
<button
185185
onClick={handleSend}
186186
disabled={sending || !text.trim()}
187-
className="px-4 py-1.5 text-sm bg-accent text-white rounded-md hover:opacity-90 disabled:opacity-40 transition-opacity"
187+
className="btn-primary-sm"
188188
>
189189
{sending ? 'Sending...' : 'Send'}
190190
</button>

ui/src/pages/EventsPage.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ function CronSection() {
330330
<span className="text-xs text-text-muted">{jobs.length} jobs</span>
331331
<button
332332
onClick={() => setShowAdd(true)}
333-
className="text-xs px-3 py-1.5 rounded-md bg-accent/20 text-accent border border-accent/30 hover:bg-accent/30 transition-colors"
333+
className="btn-secondary-sm"
334334
>
335335
+ Add Job
336336
</button>
@@ -537,7 +537,7 @@ function AddCronJobForm({ onClose, onCreated }: { onClose: () => void; onCreated
537537
<button
538538
type="submit"
539539
disabled={saving}
540-
className="px-3 py-1.5 text-sm rounded-md bg-accent text-white hover:bg-accent/80 transition-colors disabled:opacity-50"
540+
className="btn-primary-sm"
541541
>
542542
{saving ? 'Creating...' : 'Create'}
543543
</button>

ui/src/pages/HeartbeatPage.tsx

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { useState, useEffect, useCallback, useMemo } from 'react'
22
import { api, type AppConfig, type EventLogEntry } from '../api'
33
import { Toggle } from '../components/Toggle'
44
import { SaveIndicator } from '../components/SaveIndicator'
5-
import { Section, Field, inputClass } from '../components/form'
5+
import { ConfigSection, Section, Field, inputClass } from '../components/form'
66
import { useAutoSave } from '../hooks/useAutoSave'
77
import { PageHeader } from '../components/PageHeader'
88

@@ -84,7 +84,7 @@ function StatusBar() {
8484
<button
8585
onClick={handleTrigger}
8686
disabled={triggering}
87-
className="px-3 py-1.5 text-xs rounded-md bg-purple-dim text-purple border border-purple/30 hover:bg-purple/30 transition-colors disabled:opacity-50"
87+
className="btn-secondary-sm"
8888
>
8989
{triggering ? 'Triggering...' : 'Trigger Now'}
9090
</button>
@@ -98,9 +98,9 @@ function StatusBar() {
9898
)
9999
}
100100

101-
// ==================== Config Section ====================
101+
// ==================== Config Form ====================
102102

103-
function ConfigSection({ config }: { config: AppConfig }) {
103+
function HeartbeatConfigForm({ config }: { config: AppConfig }) {
104104
const [every, setEvery] = useState(config.heartbeat?.every || '30m')
105105
const [ahEnabled, setAhEnabled] = useState(config.heartbeat?.activeHours != null)
106106
const [ahStart, setAhStart] = useState(config.heartbeat?.activeHours?.start || '09:00')
@@ -120,7 +120,7 @@ function ConfigSection({ config }: { config: AppConfig }) {
120120
const { status, retry } = useAutoSave({ data: configData, save })
121121

122122
return (
123-
<Section title="Configuration">
123+
<ConfigSection title="Configuration" description="Set how often the heartbeat runs and optionally restrict it to active hours.">
124124
<Field label="Interval">
125125
<input
126126
className={inputClass}
@@ -132,7 +132,7 @@ function ConfigSection({ config }: { config: AppConfig }) {
132132

133133
<div className="mb-3">
134134
<div className="flex items-center justify-between mb-2">
135-
<label className="text-[13px] text-text-muted">Active Hours</label>
135+
<label className="text-[13px] text-text font-medium">Active Hours</label>
136136
<Toggle checked={ahEnabled} onChange={setAhEnabled} />
137137
</div>
138138
{ahEnabled && (
@@ -180,7 +180,7 @@ function ConfigSection({ config }: { config: AppConfig }) {
180180
</div>
181181

182182
<SaveIndicator status={status} onRetry={retry} />
183-
</Section>
183+
</ConfigSection>
184184
)
185185
}
186186

@@ -222,7 +222,7 @@ function PromptEditor() {
222222
}
223223

224224
return (
225-
<Section title="Prompt File" description={filePath || undefined}>
225+
<ConfigSection title="Prompt File" description={filePath || 'The prompt template used for each heartbeat cycle.'}>
226226
{loading ? (
227227
<div className="text-sm text-text-muted">Loading...</div>
228228
) : (
@@ -236,7 +236,7 @@ function PromptEditor() {
236236
<button
237237
onClick={handleSave}
238238
disabled={saving || !dirty}
239-
className="px-3 py-1.5 text-xs rounded-md bg-accent text-bg font-medium hover:opacity-90 transition-opacity disabled:opacity-40"
239+
className="btn-primary-sm"
240240
>
241241
{saving ? 'Saving...' : 'Save'}
242242
</button>
@@ -258,7 +258,7 @@ function PromptEditor() {
258258
</div>
259259
</>
260260
)}
261-
</Section>
261+
</ConfigSection>
262262
)
263263
}
264264

@@ -335,10 +335,10 @@ export function HeartbeatPage() {
335335
<div className="flex flex-col flex-1 min-h-0">
336336
<PageHeader title="Heartbeat" />
337337

338-
<div className="flex-1 overflow-y-auto px-4 md:px-6 py-5">
339-
<div className="max-w-[720px] space-y-6">
338+
<div className="flex-1 overflow-y-auto px-4 md:px-8 py-5">
339+
<div className="max-w-[880px] mx-auto space-y-6">
340340
<StatusBar />
341-
{config && <ConfigSection config={config} />}
341+
{config && <HeartbeatConfigForm config={config} />}
342342
<PromptEditor />
343343
<RecentEvents />
344344
</div>

ui/src/pages/NewsPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ function FeedsSection({
156156
<button
157157
onClick={addFeed}
158158
disabled={!newName.trim() || !newUrl.trim() || !newSource.trim()}
159-
className="border border-border rounded-lg px-4 py-2 text-[13px] font-medium cursor-pointer transition-colors hover:bg-bg-tertiary hover:text-text text-text-muted disabled:opacity-40 disabled:cursor-default"
159+
className="btn-secondary"
160160
>
161161
Add Feed
162162
</button>

ui/src/pages/ToolsPage.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,13 +103,13 @@ export function ToolsPage() {
103103
right={<SaveIndicator status={status} onRetry={retry} />}
104104
/>
105105

106-
<div className="flex-1 overflow-y-auto px-4 md:px-6 py-5">
106+
<div className="flex-1 overflow-y-auto px-4 md:px-8 py-5">
107107
{!loaded ? (
108108
<PageLoading />
109109
) : groups.length === 0 ? (
110110
<EmptyState title="No tools registered." description="Tools will appear here when the engine starts." />
111111
) : (
112-
<div className="max-w-[720px] space-y-2">
112+
<div className="max-w-[880px] mx-auto space-y-2">
113113
{groups.map((g) => (
114114
<ToolGroupCard
115115
key={g.key}

ui/src/pages/TradingPage.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -369,17 +369,17 @@ function CreateWizard({ existingAccountIds, onSave, onClose }: {
369369
</button>
370370
)}
371371
{step > 1 && (
372-
<button onClick={() => { setStep(step - 1); setError('') }} className="px-3 py-1.5 text-[13px] font-medium rounded-md border border-border hover:bg-bg-tertiary transition-colors">
372+
<button onClick={() => { setStep(step - 1); setError('') }} className="btn-secondary-sm">
373373
Back
374374
</button>
375375
)}
376376
{step === 2 && (
377-
<button onClick={handleNext} className="px-4 py-1.5 text-[13px] font-medium rounded-md bg-accent text-white hover:bg-accent/90 transition-colors">
377+
<button onClick={handleNext} className="btn-primary-sm">
378378
Next
379379
</button>
380380
)}
381381
{step === 3 && (
382-
<button onClick={handleCreate} disabled={saving} className="px-4 py-1.5 text-[13px] font-medium rounded-md bg-accent text-white hover:bg-accent/90 disabled:opacity-50 transition-colors">
382+
<button onClick={handleCreate} disabled={saving} className="btn-primary-sm">
383383
{saving ? 'Creating...' : 'Create'}
384384
</button>
385385
)}
@@ -523,7 +523,7 @@ function EditDialog({ account, platform, onSaveAccount, onSavePlatform, onDelete
523523
<button
524524
onClick={handleSave}
525525
disabled={saving}
526-
className="px-4 py-1.5 text-[13px] font-medium rounded-md bg-accent text-white hover:bg-accent/90 disabled:opacity-50 transition-colors"
526+
className="btn-primary-sm"
527527
>
528528
{saving ? 'Saving...' : 'Save'}
529529
</button>

0 commit comments

Comments
 (0)