diff --git a/AGENTS.md b/AGENTS.md index 98578ca..7f6b211 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -37,6 +37,7 @@ Standard dev commands are in `package.json` scripts and `README.md`. Key ones: - **Models page aggregation**: The 7-day/30-day filters are relative rolling windows, not calendar-month buckets. When grouped by time, the chart should keep all day buckets in the selected window; only model grouping is intentionally capped to the top entries. - **OpenClaw Doctor in UI**: In Settings > Advanced > Developer, the app exposes both `Run Doctor` (`openclaw doctor --json`) and `Run Doctor Fix` (`openclaw doctor --fix --yes --non-interactive`) through the host-api. Renderer code should call the host route, not spawn CLI processes directly. - **UI change validation**: Any user-visible UI change should include or update an Electron E2E spec in the same PR so the interaction is covered by Playwright. +- **i18n & styling conventions**: New user-facing features must (1) route all text through `react-i18next` with full locale coverage (`en` / `zh` / `ja` / `ru` under `src/i18n/locales//.json`) — never hardcode display strings, and (2) use the design tokens and substitution rules documented in `src/styles/globals.css` (surfaces `bg-surface-modal` / `bg-surface-input`, selected state `bg-black/5 dark:bg-white/10`, status colours `text-X-700 dark:text-X-400`, page H1/H2 `font-serif font-normal tracking-tight`, etc.) — see the *Component conventions* block in `globals.css` for the full substitution table. - **Renderer/Main API boundary (important)**: - Renderer must use `src/lib/host-api.ts` and `src/lib/api-client.ts` as the single entry for backend calls. - Do not add new direct `window.electron.ipcRenderer.invoke(...)` calls in pages/components; expose them through host-api/api-client instead. diff --git a/src/App.tsx b/src/App.tsx index fe9b73f..3716fd7 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -223,6 +223,7 @@ function App() { position="bottom-right" richColors closeButton + theme={theme} style={{ zIndex: 99999 }} /> diff --git a/src/components/channels/ChannelConfigModal.tsx b/src/components/channels/ChannelConfigModal.tsx index 9b663ac..a48ff5d 100644 --- a/src/components/channels/ChannelConfigModal.tsx +++ b/src/components/channels/ChannelConfigModal.tsx @@ -568,8 +568,8 @@ export function ChannelConfigModal({ {qrCode.startsWith('data:image') || qrCode.startsWith('http://') || qrCode.startsWith('https://') ? ( Scan QR Code ) : ( -
- +
+
)}
diff --git a/src/components/common/ErrorBoundary.tsx b/src/components/common/ErrorBoundary.tsx index 83397d8..ba7c39a 100644 --- a/src/components/common/ErrorBoundary.tsx +++ b/src/components/common/ErrorBoundary.tsx @@ -55,7 +55,7 @@ export class ErrorBoundary extends Component { {this.state.error && ( -
+                
                   {this.state.error.message}
                 
)} diff --git a/src/components/common/StatusBadge.tsx b/src/components/common/StatusBadge.tsx index 8eb2e89..f071ae2 100644 --- a/src/components/common/StatusBadge.tsx +++ b/src/components/common/StatusBadge.tsx @@ -35,7 +35,7 @@ export function StatusBadge({ status, label, showDot = true }: StatusBadgeProps) className={cn( 'h-1.5 w-1.5 rounded-full', config.variant === 'success' && 'bg-green-600', - config.variant === 'secondary' && 'bg-gray-400', + config.variant === 'secondary' && 'bg-muted-foreground', config.variant === 'warning' && 'bg-yellow-600 animate-pulse', config.variant === 'destructive' && 'bg-red-600' )} diff --git a/src/components/file-preview/GeneratedFilesPanel.tsx b/src/components/file-preview/GeneratedFilesPanel.tsx index 2f03a1c..4a26263 100644 --- a/src/components/file-preview/GeneratedFilesPanel.tsx +++ b/src/components/file-preview/GeneratedFilesPanel.tsx @@ -66,7 +66,7 @@ export function GeneratedFilesPanel({ )} title={file.filePath} > -
+
{file.fileName} {file.filePath}
@@ -96,7 +96,7 @@ export function GeneratedFilesPanel({ )} title={file.filePath} > -
+
{file.fileName} {file.filePath}
diff --git a/src/components/file-preview/PdfViewer.tsx b/src/components/file-preview/PdfViewer.tsx index 50335f4..582e6d7 100644 --- a/src/components/file-preview/PdfViewer.tsx +++ b/src/components/file-preview/PdfViewer.tsx @@ -150,8 +150,8 @@ export default function PdfViewer({ className={cn( 'relative h-full min-h-0 overflow-hidden', workspaceSurface - ? 'overflow-auto bg-[hsl(var(--muted)/0.35)] p-4 dark:bg-background' - : 'bg-white', + ? 'overflow-auto bg-surface-input/35 p-4' + : 'bg-surface-modal', className, )} > @@ -159,7 +159,7 @@ export default function PdfViewer({
diff --git a/src/components/file-preview/SheetViewer.tsx b/src/components/file-preview/SheetViewer.tsx index 4849351..5d38ab3 100644 --- a/src/components/file-preview/SheetViewer.tsx +++ b/src/components/file-preview/SheetViewer.tsx @@ -219,16 +219,16 @@ export default function SheetViewer({ filePath, fileName, className }: SheetView
) : ( - +
{activeSheet.columnLetters.map((letter, idx) => ( {letter} @@ -264,7 +264,7 @@ export default function SheetViewer({ filePath, fileName, className }: SheetView
)}
-
+
{sheetTabs}
{totalPages > 1 && (
diff --git a/src/components/settings/ImageGenerationSettings.tsx b/src/components/settings/ImageGenerationSettings.tsx index 2d4ccdf..29ab20d 100644 --- a/src/components/settings/ImageGenerationSettings.tsx +++ b/src/components/settings/ImageGenerationSettings.tsx @@ -350,7 +350,7 @@ export function ImageGenerationSettings() { {agent.name} {agent.isDefault ? ( - + {t('imageGeneration.defaultAgent')} ) : null} diff --git a/src/components/settings/ProvidersSettings.tsx b/src/components/settings/ProvidersSettings.tsx index b71c736..ebf9b75 100644 --- a/src/components/settings/ProvidersSettings.tsx +++ b/src/components/settings/ProvidersSettings.tsx @@ -531,7 +531,7 @@ function ProviderCard({ }; const currentInputClasses = isDefault - ? "h-[40px] rounded-xl font-mono text-meta bg-white dark:bg-card border-black/10 dark:border-white/10 focus-visible:ring-2 focus-visible:ring-blue-500/50 shadow-sm" + ? "h-[40px] rounded-xl font-mono text-meta bg-surface-modal border-black/10 dark:border-white/10 focus-visible:ring-2 focus-visible:ring-blue-500/50 shadow-sm" : inputClasses; const currentLabelClasses = isDefault ? "text-meta text-muted-foreground" : labelClasses; @@ -608,7 +608,7 @@ function ProviderCard({ data-testid={`provider-set-default-${account.id}`} variant="ghost" size="icon" - className="h-8 w-8 rounded-full text-muted-foreground hover:text-blue-600 hover:bg-white dark:hover:bg-card shadow-sm" + className="h-8 w-8 rounded-full text-muted-foreground hover:text-blue-600 hover:bg-surface-modal shadow-sm" onClick={onSetDefault} title={t('aiProviders.card.setDefault')} > @@ -619,7 +619,7 @@ function ProviderCard({ data-testid={`provider-edit-${account.id}`} variant="ghost" size="icon" - className="h-8 w-8 rounded-full text-muted-foreground hover:text-foreground hover:bg-white dark:hover:bg-card shadow-sm" + className="h-8 w-8 rounded-full text-muted-foreground hover:text-foreground hover:bg-surface-modal shadow-sm" onClick={onEdit} title={t('aiProviders.card.editKey')} > @@ -629,7 +629,7 @@ function ProviderCard({ data-testid={`provider-delete-${account.id}`} variant="ghost" size="icon" - className="h-8 w-8 rounded-full text-muted-foreground hover:text-destructive hover:bg-white dark:hover:bg-card shadow-sm" + className="h-8 w-8 rounded-full text-muted-foreground hover:text-destructive hover:bg-surface-modal shadow-sm" onClick={onDelete} title={t('aiProviders.card.delete')} > @@ -708,7 +708,7 @@ function ProviderCard({ setModelId(typeInfo?.defaultModelId || ''); } }} - className={cn("flex-1 py-1.5 px-3 rounded-lg border transition-colors", arkMode === 'apikey' ? "bg-white dark:bg-card border-black/20 dark:border-white/20 shadow-sm font-medium" : "border-transparent bg-black/5 dark:bg-white/5 text-muted-foreground hover:bg-black/10 dark:hover:bg-white/10")} + className={cn("flex-1 py-1.5 px-3 rounded-lg border transition-colors", arkMode === 'apikey' ? "bg-surface-modal border-black/20 dark:border-white/20 shadow-sm font-medium" : "border-transparent bg-black/5 dark:bg-white/5 text-muted-foreground hover:bg-black/10 dark:hover:bg-white/10")} > {t('aiProviders.authModes.apiKey')} @@ -719,7 +719,7 @@ function ProviderCard({ setBaseUrl(codePlanPreset.baseUrl); setModelId(codePlanPreset.modelId); }} - className={cn("flex-1 py-1.5 px-3 rounded-lg border transition-colors", arkMode === 'codeplan' ? "bg-white dark:bg-card border-black/20 dark:border-white/20 shadow-sm font-medium" : "border-transparent bg-black/5 dark:bg-white/5 text-muted-foreground hover:bg-black/10 dark:hover:bg-white/10")} + className={cn("flex-1 py-1.5 px-3 rounded-lg border transition-colors", arkMode === 'codeplan' ? "bg-surface-modal border-black/20 dark:border-white/20 shadow-sm font-medium" : "border-transparent bg-black/5 dark:bg-white/5 text-muted-foreground hover:bg-black/10 dark:hover:bg-white/10")} > {t('aiProviders.dialog.codePlanMode')} @@ -738,21 +738,21 @@ function ProviderCard({ @@ -789,7 +789,7 @@ function ProviderCard({ onChange={(e) => setFallbackModelsText(e.target.value)} placeholder={t('aiProviders.dialog.fallbackModelIdsPlaceholder')} className={isDefault - ? "min-h-24 w-full rounded-xl border border-black/10 dark:border-white/10 bg-white dark:bg-card px-3 py-2 text-meta font-mono outline-none focus-visible:ring-2 focus-visible:ring-blue-500/50 shadow-sm" + ? "min-h-24 w-full rounded-xl border border-black/10 dark:border-white/10 bg-surface-modal px-3 py-2 text-meta font-mono outline-none focus-visible:ring-2 focus-visible:ring-blue-500/50 shadow-sm" : "min-h-24 w-full rounded-xl border border-black/10 dark:border-white/10 bg-transparent px-3 py-2 text-meta font-mono outline-none focus-visible:ring-2 focus-visible:ring-blue-500/50 focus-visible:border-blue-500 shadow-sm transition-all text-foreground placeholder:text-foreground/40"} />

@@ -801,7 +801,7 @@ function ProviderCard({ {fallbackOptions.length === 0 ? (

{t('aiProviders.dialog.noFallbackOptions')}

) : ( -
+
{fallbackOptions.map((candidate) => (