Skip to content

Commit bf68d5e

Browse files
committed
fix: resolved i18n gaps, a11y violations, and layout inconsistencies
1 parent 200e087 commit bf68d5e

10 files changed

Lines changed: 139 additions & 54 deletions

File tree

.gitignore

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,17 @@ yarn-error.log*
3434
pnpm-debug.log*
3535

3636
# Local configurations
37+
.github/prompts/
38+
.ui-design/
3739
*.local.md
40+
.opencode
41+
openspec
3842
.cursor
3943
.claude
44+
skills/
4045
.codex
4146
.roo
42-
skills/
47+
.agent
4348

4449
# Environment
4550
.env

src/renderer/components/layout/StartupSplash.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
/**
22
* @file src/renderer/components/layout/StartupSplash.tsx
33
*
4+
* @created 07.03.2026
5+
* @modified 16.03.2026
6+
*
7+
* @author Christian Blank <aidrelay@proton.me>
8+
* @copyright 2026
9+
*
410
* @description Full-screen startup splash overlay with a milestone progress
511
* bar and loading text.
612
*/

src/renderer/components/rules/RuleEditor.tsx

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* @file src/renderer/components/rules/RuleEditor.tsx
33
*
44
* @created 07.03.2026
5-
* @modified 08.03.2026
5+
* @modified 16.03.2026
66
*
77
* @author Christian Blank <aidrelay@proton.me>
88
* @copyright 2026
@@ -14,9 +14,10 @@
1414
* the rules Zustand store.
1515
*/
1616

17-
import { useState, useCallback } from 'react'
17+
import { useState, useCallback, useEffect } from 'react'
1818
import { X } from 'lucide-react'
1919
import { toast } from 'sonner'
20+
import { useTranslation } from 'react-i18next'
2021
import { cn } from '@/lib/utils'
2122
import { Button } from '@/components/ui/button'
2223
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
@@ -51,18 +52,27 @@ const RuleEditor = ({ rule, onClose }: RuleEditorProps) => {
5152
const [activeTab, setActiveTab] = useState<Tab>('details')
5253
const [saving, setSaving] = useState(false)
5354
const [content, setContent] = useState(rule?.content ?? '')
55+
const { t } = useTranslation()
5456

5557
const { create, update } = useRulesStore()
5658
const tokenEstimate = useTokenEstimate(content)
5759

60+
useEffect(() => {
61+
const handleKeyDown = (event: KeyboardEvent) => {
62+
if (event.key === 'Escape') onClose()
63+
}
64+
document.addEventListener('keydown', handleKeyDown)
65+
return () => document.removeEventListener('keydown', handleKeyDown)
66+
}, [onClose])
67+
5868
const handleContentChange = useCallback((newContent: string) => {
5969
setContent(newContent)
6070
}, [])
6171

6272
const handleDetailsSubmit = useCallback(
6373
async (data: Omit<CreateRuleInput, 'content'>) => {
6474
if (!content.trim()) {
65-
toast.error('Rule content cannot be empty')
75+
toast.error(t('ruleEditor.contentRequired'))
6676
return
6777
}
6878

@@ -87,7 +97,7 @@ const RuleEditor = ({ rule, onClose }: RuleEditorProps) => {
8797
setSaving(false)
8898
}
8999
},
90-
[content, rule, create, update, onClose],
100+
[content, rule, create, update, onClose, t],
91101
)
92102

93103
return (
@@ -105,13 +115,21 @@ const RuleEditor = ({ rule, onClose }: RuleEditorProps) => {
105115
className="fixed inset-y-0 right-0 z-50 w-full max-w-2xl flex flex-col bg-background border-l border-border shadow-xl"
106116
role="dialog"
107117
aria-modal="true"
108-
aria-label={rule ? `Edit rule: ${rule.name}` : 'Add rule'}
118+
aria-label={
119+
rule
120+
? t('ruleEditor.editTitleWithName', { name: rule.name })
121+
: t('ruleEditor.createTitle')
122+
}
109123
data-testid="rule-editor"
110124
>
111125
{/* Header */}
112126
<header className="flex items-center justify-between px-6 py-4 border-b border-border">
113127
<div className="flex items-center gap-3">
114-
<h2 className="font-semibold text-base">{rule ? `Edit: ${rule.name}` : 'Add rule'}</h2>
128+
<h2 className="font-semibold text-base">
129+
{rule
130+
? t('ruleEditor.editTitleWithName', { name: rule.name })
131+
: t('ruleEditor.createTitle')}
132+
</h2>
115133
{tokenEstimate > 0 && (
116134
<span
117135
className={cn(
@@ -131,13 +149,13 @@ const RuleEditor = ({ rule, onClose }: RuleEditorProps) => {
131149
variant="ghost"
132150
size="icon"
133151
onClick={onClose}
134-
aria-label="Close editor"
152+
aria-label={t('ruleEditor.close')}
135153
data-testid="rule-editor-close"
136154
>
137155
<X size={18} />
138156
</Button>
139157
</TooltipTrigger>
140-
<TooltipContent>Close</TooltipContent>
158+
<TooltipContent>{t('ruleEditor.close')}</TooltipContent>
141159
</Tooltip>
142160
</header>
143161

@@ -156,14 +174,14 @@ const RuleEditor = ({ rule, onClose }: RuleEditorProps) => {
156174
className="rounded-none border-b-2 border-transparent data-[state=active]:border-primary data-[state=active]:bg-transparent"
157175
data-testid="rule-editor-tab-details"
158176
>
159-
Details
177+
{t('ruleEditor.tabDetails')}
160178
</TabsTrigger>
161179
<TabsTrigger
162180
value="content"
163181
className="rounded-none border-b-2 border-transparent data-[state=active]:border-primary data-[state=active]:bg-transparent"
164182
data-testid="rule-editor-tab-content"
165183
>
166-
Content
184+
{t('ruleEditor.tabContent')}
167185
</TabsTrigger>
168186
</TabsList>
169187
<TabsContent value="details" className="flex-1 overflow-y-auto px-6 py-5 mt-0">
@@ -190,14 +208,14 @@ const RuleEditor = ({ rule, onClose }: RuleEditorProps) => {
190208
onClick={onClose}
191209
data-testid="rule-editor-content-cancel"
192210
>
193-
Cancel
211+
{t('ruleEditor.cancel')}
194212
</Button>
195213
<Button
196214
type="button"
197215
onClick={() => setActiveTab('details')}
198216
data-testid="rule-editor-content-next"
199217
>
200-
{rule ? 'Save changes' : 'Continue to details →'}
218+
{t('ruleEditor.continueToDetails')}
201219
</Button>
202220
</footer>
203221
)}

src/renderer/components/servers/ServerEditor.tsx

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* @file src/renderer/components/servers/ServerEditor.tsx
33
*
44
* @created 07.03.2026
5-
* @modified 08.03.2026
5+
* @modified 16.03.2026
66
*
77
* @author Christian Blank <aidrelay@proton.me>
88
* @copyright 2026
@@ -13,9 +13,10 @@
1313
* `serversCreate` or `serversUpdate` from the servers Zustand store.
1414
*/
1515

16-
import { useState, useCallback } from 'react'
16+
import { useState, useCallback, useEffect } from 'react'
1717
import { X } from 'lucide-react'
1818
import { toast } from 'sonner'
19+
import { useTranslation } from 'react-i18next'
1920
import { Button } from '@/components/ui/button'
2021
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
2122
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
@@ -47,9 +48,18 @@ const ServerEditor = ({ server, onClose }: ServerEditorProps) => {
4748
const [activeTab, setActiveTab] = useState<Tab>('form')
4849
const [saving, setSaving] = useState(false)
4950
const [formState, setFormState] = useState<McpServer | undefined>(server)
51+
const { t } = useTranslation()
5052

5153
const { create, update } = useServersStore()
5254

55+
useEffect(() => {
56+
const handleKeyDown = (event: KeyboardEvent) => {
57+
if (event.key === 'Escape') onClose()
58+
}
59+
document.addEventListener('keydown', handleKeyDown)
60+
return () => document.removeEventListener('keydown', handleKeyDown)
61+
}, [onClose])
62+
5363
const handleFormSubmit = useCallback(
5464
async (data: CreateServerInput & { secretEnvKeys: string[] }) => {
5565
setSaving(true)
@@ -104,13 +114,19 @@ const ServerEditor = ({ server, onClose }: ServerEditorProps) => {
104114
className="fixed inset-y-0 right-0 z-50 w-full max-w-xl flex flex-col bg-background border-l border-border shadow-xl"
105115
role="dialog"
106116
aria-modal="true"
107-
aria-label={server ? `Edit MCP Server: ${server.name}` : 'Add MCP Server'}
117+
aria-label={
118+
server
119+
? t('serverForm.editTitleWithName', { name: server.name })
120+
: t('serverForm.createTitle')
121+
}
108122
data-testid="server-editor"
109123
>
110124
{/* Header */}
111125
<header className="flex items-center justify-between px-6 py-4 border-b border-border">
112126
<h2 className="font-semibold text-base">
113-
{server ? `Edit MCP Server: ${server.name}` : 'Add MCP Server'}
127+
{server
128+
? t('serverForm.editTitleWithName', { name: server.name })
129+
: t('serverForm.createTitle')}
114130
</h2>
115131
<Tooltip>
116132
<TooltipTrigger asChild>
@@ -119,13 +135,13 @@ const ServerEditor = ({ server, onClose }: ServerEditorProps) => {
119135
variant="ghost"
120136
size="icon"
121137
onClick={onClose}
122-
aria-label="Close editor"
138+
aria-label={t('serverForm.close')}
123139
data-testid="server-editor-close"
124140
>
125141
<X size={18} />
126142
</Button>
127143
</TooltipTrigger>
128-
<TooltipContent>Close</TooltipContent>
144+
<TooltipContent>{t('serverForm.close')}</TooltipContent>
129145
</Tooltip>
130146
</header>
131147

@@ -144,14 +160,14 @@ const ServerEditor = ({ server, onClose }: ServerEditorProps) => {
144160
className="rounded-none border-b-2 border-transparent data-[state=active]:border-primary data-[state=active]:bg-transparent"
145161
data-testid="server-editor-tab-form"
146162
>
147-
Form
163+
{t('serverForm.tabForm')}
148164
</TabsTrigger>
149165
<TabsTrigger
150166
value="json"
151167
className="rounded-none border-b-2 border-transparent data-[state=active]:border-primary data-[state=active]:bg-transparent"
152168
data-testid="server-editor-tab-json"
153169
>
154-
JSON
170+
{t('serverForm.tabJson')}
155171
</TabsTrigger>
156172
</TabsList>
157173
<TabsContent value="form" className="flex-1 overflow-y-auto px-6 py-5 mt-0">
@@ -187,7 +203,7 @@ const ServerEditor = ({ server, onClose }: ServerEditorProps) => {
187203
{activeTab === 'json' && (
188204
<footer className="flex justify-end gap-2 px-6 py-4 border-t border-border">
189205
<Button type="button" variant="outline" onClick={onClose}>
190-
Cancel
206+
{t('serverForm.cancel')}
191207
</Button>
192208
<Button
193209
type="button"
@@ -208,7 +224,11 @@ const ServerEditor = ({ server, onClose }: ServerEditorProps) => {
208224
disabled={saving || !formState?.name || !formState?.command}
209225
data-testid="server-editor-json-save"
210226
>
211-
{saving ? 'Saving…' : server ? 'Save changes' : 'Add MCP Server'}
227+
{saving
228+
? t('serverForm.saving')
229+
: server
230+
? t('serverForm.saveChanges')
231+
: t('serverForm.addServer')}
212232
</Button>
213233
</footer>
214234
)}

src/renderer/i18n/de.json

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,12 @@
231231
"cancel": "Abbrechen",
232232
"viewJson": "JSON anzeigen",
233233
"viewForm": "Formular anzeigen",
234-
"close": "Schließen"
234+
"close": "Schließen",
235+
"tabForm": "Formular",
236+
"tabJson": "JSON",
237+
"editTitleWithName": "MCP-Server bearbeiten: {{name}}",
238+
"saveChanges": "Änderungen speichern",
239+
"addServer": "MCP-Server hinzufügen"
235240
},
236241
"envEditor": {
237242
"addVariable": "Variable hinzufügen",
@@ -325,7 +330,9 @@
325330
"tabDetails": "Details",
326331
"tabContent": "Inhalt",
327332
"saveChanges": "Änderungen speichern",
328-
"continueToDetails": "Weiter zu Details →"
333+
"continueToDetails": "Weiter zu Details →",
334+
"editTitleWithName": "Bearbeiten: {{name}}",
335+
"contentRequired": "Regelinhalt darf nicht leer sein"
329336
},
330337
"profiles": {
331338
"title": "Profile",
@@ -397,7 +404,8 @@
397404
"collapseDetails": "Details einklappen",
398405
"expandDetails": "Details ausklappen",
399406
"showDetails": "Anzeigen",
400-
"hideDetails": "Verbergen"
407+
"hideDetails": "Verbergen",
408+
"loadFailed": "Aktivitätsprotokoll konnte nicht geladen werden"
401409
},
402410
"clients": {
403411
"title": "Clients",

src/renderer/i18n/en.json

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,12 @@
231231
"cancel": "Cancel",
232232
"viewJson": "View JSON",
233233
"viewForm": "View form",
234-
"close": "Close"
234+
"close": "Close",
235+
"tabForm": "Form",
236+
"tabJson": "JSON",
237+
"editTitleWithName": "Edit MCP Server: {{name}}",
238+
"saveChanges": "Save changes",
239+
"addServer": "Add MCP Server"
235240
},
236241
"envEditor": {
237242
"addVariable": "Add variable",
@@ -325,7 +330,9 @@
325330
"tabDetails": "Details",
326331
"tabContent": "Content",
327332
"saveChanges": "Save changes",
328-
"continueToDetails": "Continue to details →"
333+
"continueToDetails": "Continue to details →",
334+
"editTitleWithName": "Edit: {{name}}",
335+
"contentRequired": "Rule content cannot be empty"
329336
},
330337
"profiles": {
331338
"title": "Profiles",
@@ -397,7 +404,8 @@
397404
"collapseDetails": "Collapse details",
398405
"expandDetails": "Expand details",
399406
"showDetails": "Show",
400-
"hideDetails": "Hide"
407+
"hideDetails": "Hide",
408+
"loadFailed": "Failed to load activity log"
401409
},
402410
"clients": {
403411
"title": "Clients",

src/renderer/pages/ActivityLogPage.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* @file src/renderer/pages/ActivityLogPage.tsx
33
*
44
* @created 07.03.2026
5-
* @modified 10.03.2026
5+
* @modified 16.03.2026
66
*
77
* @author Christian Blank <aidrelay@proton.me>
88
* @copyright 2026
@@ -15,6 +15,7 @@
1515

1616
import { useState, useEffect, useCallback } from 'react'
1717
import { RefreshCw } from 'lucide-react'
18+
import { toast } from 'sonner'
1819
import { useTranslation } from 'react-i18next'
1920
import { Button } from '@/components/ui/button'
2021
import { Input } from '@/components/ui/input'
@@ -59,11 +60,12 @@ const ActivityLogPage = () => {
5960
const results = await logService.query(filters)
6061
setEntries(results)
6162
} catch (err) {
62-
console.error('Failed to load activity log:', err)
63+
const message = err instanceof Error ? err.message : t('common.error')
64+
toast.error(t('activityLog.loadFailed'), { description: message })
6365
} finally {
6466
setLoading(false)
6567
}
66-
}, [actionFilter, clientFilter, sinceFilter])
68+
}, [actionFilter, clientFilter, sinceFilter, t])
6769

6870
useEffect(() => {
6971
void fetchEntries()

0 commit comments

Comments
 (0)