From d604be3bcbb9068facfc09ebabd7710cd8245e65 Mon Sep 17 00:00:00 2001 From: Douglas Lai <115660088+Douglasymlai@users.noreply.github.com> Date: Tue, 21 Apr 2026 16:13:01 +0100 Subject: [PATCH] Design Roken refactor (#1569) Co-authored-by: Claude Opus 4.7 --- electron/main/index.ts | 10 +- package.json | 1 + scripts/verify-theme-tokens.ts | 195 ++++ src/components/AddWorker/ToolSelect.tsx | 19 +- src/components/AddWorker/index.tsx | 20 +- .../Background/DotPatternBackground.tsx | 2 +- .../Background/GridPatternBackground.tsx | 2 +- .../BrowserAgentWorkspace/index.tsx | 26 +- .../BottomBox/ChatInputModelDropdown.tsx | 47 +- src/components/ChatBox/BottomBox/InputBox.tsx | 71 +- .../ChatBox/BottomBox/RichChatInput.tsx | 2 +- src/components/ChatBox/BottomBox/index.tsx | 20 +- src/components/ChatBox/FloatingAction.tsx | 4 +- .../ChatBox/MessageItem/FeedbackCard.tsx | 2 +- .../MessageItem/TaskCompletionCard.tsx | 10 +- .../ChatBox/MessageItem/UserMessageCard.tsx | 15 +- .../ChatBox/TaskBox/StreamingTaskList.tsx | 20 +- src/components/ChatBox/TaskBox/TaskCard.tsx | 4 +- src/components/ChatBox/TaskBox/TaskItem.tsx | 2 +- .../ChatBox/TaskBox/TypeCardSkeleton.tsx | 18 +- .../GroupedHistoryView/ProjectDialog.tsx | 4 +- .../GroupedHistoryView/ProjectGroup.tsx | 107 +- .../Dashboard/GroupedHistoryView/TaskItem.tsx | 19 +- .../Dashboard/GroupedHistoryView/index.tsx | 74 +- src/components/Dashboard/HistoryTabsNav.tsx | 233 ++++ .../Dashboard/SearchInput/index.tsx | 2 +- .../{Navigation/index.tsx => VerticalNav.tsx} | 6 +- src/components/Dialog/CloseNotice.tsx | 2 +- src/components/Dialog/EndNotice.tsx | 4 +- src/components/Dialog/SearchHistoryDialog.tsx | 4 +- src/components/Folder/ZoomControls.tsx | 10 +- src/components/Folder/index.tsx | 582 ++++++++-- src/components/HistorySidebar/index.tsx | 40 +- src/components/InstallStep/Carousel.tsx | 4 +- .../InstallStep/InstallDependencies.tsx | 4 +- .../InstallationErrorDialog.tsx | 4 +- src/components/Layout/ThemeProvider.tsx | 154 ++- src/components/Layout/tokenAliases.ts | 60 + .../ProjectPageSidebar/BottomAction.tsx | 6 +- .../ProjectPageSidebar/HeaderAction.tsx | 15 +- src/components/ProjectPageSidebar/NavList.tsx | 40 +- src/components/ProjectPageSidebar/NavTab.tsx | 29 +- src/components/ProjectPageSidebar/index.tsx | 16 +- src/components/Session/SessionGroup.tsx | 2 +- .../Session/SidePanelAccordionBox.tsx | 4 +- .../SingleAgent/SingleAgentSidePanel.tsx | 2 +- src/components/Terminal/index.tsx | 4 +- .../TerminalAgentWorkspace/index.tsx | 30 +- src/components/TopBar/index.tsx | 124 +- .../Trigger/DynamicTriggerConfig.tsx | 4 +- src/components/Trigger/ExecutionLogs.tsx | 2 +- src/components/Trigger/TriggerDialog.tsx | 1 + src/components/Trigger/TriggerListItem.tsx | 6 +- src/components/Trigger/Triggers.tsx | 16 +- src/components/Trigger/index.tsx | 6 +- src/components/WorkFlow/MarkDown.tsx | 20 +- .../WorkFlow/TaskLogPanelContent.tsx | 2 +- src/components/WorkFlow/agents.tsx | 24 +- src/components/WorkFlow/node.tsx | 106 +- src/components/Workforce/ExpandedOverlay.tsx | 8 +- .../Workforce/WorkforceSidePanel.tsx | 4 +- src/components/WorkforceMenu/index.tsx | 24 +- src/components/Workspace/FoldedAgentCard.tsx | 13 +- src/components/Workspace/SingleAgentList.tsx | 2 +- .../Workspace/WorkforceAgentList.tsx | 4 +- .../Workspace/WorkspaceExamplePrompts.tsx | 2 +- .../Workspace/WorkspaceProjectPicker.tsx | 10 +- .../Workspace/WorkspaceSessionModeToggle.tsx | 243 ++-- src/components/Workspace/index.tsx | 62 +- src/components/ui/accordion.tsx | 6 +- src/components/ui/alert.tsx | 7 +- src/components/ui/alertDialog.tsx | 20 +- src/components/ui/badge.tsx | 187 ++- src/components/ui/button.stories.tsx | 83 +- src/components/ui/button.tsx | 407 ++++++- src/components/ui/card.tsx | 8 +- src/components/ui/checkbox.tsx | 6 +- src/components/ui/command.tsx | 21 +- src/components/ui/dialog.tsx | 8 +- src/components/ui/dropdown-menu.tsx | 21 +- src/components/ui/input-select.tsx | 398 ++++--- src/components/ui/input.tsx | 63 +- src/components/ui/menu-button.tsx | 8 +- src/components/ui/popover.tsx | 20 +- src/components/ui/resizable.tsx | 4 +- src/components/ui/select.tsx | 83 +- src/components/ui/semanticProps.ts | 49 + src/components/ui/separator.tsx | 2 +- src/components/ui/sheet.tsx | 17 +- src/components/ui/sidebar.tsx | 48 +- src/components/ui/sonner.tsx | 8 +- src/components/ui/switch.tsx | 8 +- src/components/ui/table.tsx | 74 +- src/components/ui/tabs.tsx | 18 +- src/components/ui/tag.tsx | 348 +++++- src/components/ui/textarea.tsx | 66 +- src/components/ui/toggle.tsx | 4 +- src/components/ui/tokenAliases.ts | 141 +++ src/components/ui/tooltip.tsx | 7 +- src/components/update/index.tsx | 2 +- src/i18n/locales/ar/layout.json | 3 + src/i18n/locales/ar/setting.json | 14 + src/i18n/locales/de/layout.json | 3 + src/i18n/locales/de/setting.json | 14 + src/i18n/locales/en-us/layout.json | 11 + src/i18n/locales/en-us/setting.json | 14 + src/i18n/locales/es/layout.json | 3 + src/i18n/locales/es/setting.json | 14 + src/i18n/locales/fr/layout.json | 3 + src/i18n/locales/fr/setting.json | 14 + src/i18n/locales/it/layout.json | 3 + src/i18n/locales/it/setting.json | 14 + src/i18n/locales/ja/layout.json | 3 + src/i18n/locales/ja/setting.json | 14 + src/i18n/locales/ko/layout.json | 3 + src/i18n/locales/ko/setting.json | 14 + src/i18n/locales/ru/layout.json | 3 + src/i18n/locales/ru/setting.json | 14 + src/i18n/locales/zh-Hans/layout.json | 3 + src/i18n/locales/zh-Hans/setting.json | 7 + src/i18n/locales/zh-Hant/layout.json | 3 + src/i18n/locales/zh-Hant/setting.json | 7 + src/lib/themeTokens/README.md | 68 ++ src/lib/themeTokens/catalog.ts | 158 +++ src/lib/themeTokens/colorMath.ts | 266 +++++ src/lib/themeTokens/dtcg.ts | 150 +++ src/lib/themeTokens/engine.ts | 1005 +++++++++++++++++ src/lib/themeTokens/index.ts | 19 + src/lib/themeTokens/naming.ts | 23 + src/lib/themeTokens/types.ts | 142 +++ src/lib/themeTokens/verifier.ts | 407 +++++++ src/pages/Agents/Models.tsx | 48 +- .../Agents/components/SkillDeleteDialog.tsx | 2 +- .../Agents/components/SkillUploadDialog.tsx | 2 +- src/pages/Agents/index.tsx | 50 +- src/pages/Browser/CDP.tsx | 151 +-- src/pages/Browser/Cookies.tsx | 2 +- src/pages/Browser/Extension.tsx | 14 +- src/pages/Browser/index.tsx | 50 +- src/pages/Channels/index.tsx | 80 +- src/pages/Connectors/index.tsx | 48 +- src/pages/History.tsx | 128 +-- src/pages/Home.tsx | 2 +- src/pages/Projects/Project.tsx | 234 ++-- src/pages/Setting/Appearance.tsx | 476 ++++++++ src/pages/Setting/General.tsx | 103 +- src/pages/Setting/index.tsx | 108 +- src/store/authStore.ts | 129 ++- src/style/index.css | 51 +- src/style/token.css | 585 +--------- src/style/tokens/base.color.json | 279 +++++ src/style/tokens/component.color.json | 403 +++++++ src/style/tokens/contracts/default.base.json | 15 + src/style/tokens/contracts/default.dark.json | 4 + src/style/tokens/contracts/default.light.json | 4 + src/style/tokens/manifest.json | 29 + src/style/tokens/semantic.color.json | 215 ++++ tailwind.config.js | 231 +++- .../components/Layout/ThemeProvider.test.tsx | 101 ++ test/unit/lib/themeTokens/engine.v2.test.ts | 395 +++++++ test/unit/lib/themeTokens/verifier.test.ts | 65 ++ tsconfig.node.json | 2 +- 162 files changed, 8697 insertions(+), 2504 deletions(-) create mode 100644 scripts/verify-theme-tokens.ts create mode 100644 src/components/Dashboard/HistoryTabsNav.tsx rename src/components/Dashboard/{Navigation/index.tsx => VerticalNav.tsx} (89%) create mode 100644 src/components/Layout/tokenAliases.ts create mode 100644 src/components/ui/semanticProps.ts create mode 100644 src/components/ui/tokenAliases.ts create mode 100644 src/lib/themeTokens/README.md create mode 100644 src/lib/themeTokens/catalog.ts create mode 100644 src/lib/themeTokens/colorMath.ts create mode 100644 src/lib/themeTokens/dtcg.ts create mode 100644 src/lib/themeTokens/engine.ts create mode 100644 src/lib/themeTokens/index.ts create mode 100644 src/lib/themeTokens/naming.ts create mode 100644 src/lib/themeTokens/types.ts create mode 100644 src/lib/themeTokens/verifier.ts create mode 100644 src/pages/Setting/Appearance.tsx create mode 100644 src/style/tokens/base.color.json create mode 100644 src/style/tokens/component.color.json create mode 100644 src/style/tokens/contracts/default.base.json create mode 100644 src/style/tokens/contracts/default.dark.json create mode 100644 src/style/tokens/contracts/default.light.json create mode 100644 src/style/tokens/manifest.json create mode 100644 src/style/tokens/semantic.color.json create mode 100644 test/unit/components/Layout/ThemeProvider.test.tsx create mode 100644 test/unit/lib/themeTokens/engine.v2.test.ts create mode 100644 test/unit/lib/themeTokens/verifier.test.ts diff --git a/electron/main/index.ts b/electron/main/index.ts index f6c49584..75a52aa5 100644 --- a/electron/main/index.ts +++ b/electron/main/index.ts @@ -413,13 +413,8 @@ protocol.registerSchemesAsPrivileged([ process.env.APP_ROOT = MAIN_DIST; process.env.VITE_PUBLIC = VITE_PUBLIC; -// Respect system theme on Windows, keep light theme on macOS for consistency -const isWindows = process.platform === 'win32'; -if (isWindows) { - nativeTheme.themeSource = 'system'; // Respect Windows dark/light mode -} else { - nativeTheme.themeSource = 'light'; // Keep existing behavior for macOS -} +// Always follow OS appearance so renderer `prefers-color-scheme` stays accurate. +nativeTheme.themeSource = 'system'; // Set log level log.transports.console.level = 'info'; @@ -2737,6 +2732,7 @@ let installationLock: Promise = Promise.resolve({ // ==================== window create ==================== async function createWindow() { const isMac = process.platform === 'darwin'; + const isWindows = process.platform === 'win32'; // Ensure .eigent directories exist before anything else ensureEigentDirectories(); diff --git a/package.json b/package.json index a21c7821..a112aa90 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "test:e2e": "vitest run --config vitest.config.ts", "test:coverage": "vitest run --coverage", "check:i18n": "node scripts/check-i18n-locale-parity.js", + "verify:theme": "vite-node scripts/verify-theme-tokens.ts", "type-check": "tsc -p tsconfig.build.json --noEmit", "lint": "eslint . --no-warn-ignored", "lint:fix": "eslint . --fix --no-warn-ignored", diff --git a/scripts/verify-theme-tokens.ts b/scripts/verify-theme-tokens.ts new file mode 100644 index 00000000..da70a125 --- /dev/null +++ b/scripts/verify-theme-tokens.ts @@ -0,0 +1,195 @@ +// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. ========= +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. ========= + +// Standalone V2 design-token verification CLI. +// +// Runs the verifier over every registered theme/mode/contrast variant and +// prints a human-readable report. Exits with a non-zero code if any `error` +// findings are produced. Auxiliary contrast warnings do not fail the run +// unless `--strict` is passed. +// +// Usage: +// npm run verify:theme +// npm run verify:theme -- --strict # auxiliary warnings fail too +// npm run verify:theme -- --json # machine-readable output +// npm run verify:theme -- --contrast 0,50,100 + +import { + getDefaultContrastGrid, + listRegisteredThemes, + verifyThemeEngine, + type VerifyFinding, +} from '../src/lib/themeTokens/verifier'; + +type CliFlags = { + strict: boolean; + json: boolean; + contrastGrid: number[]; +}; + +function parseArgs(argv: string[]): CliFlags { + const flags: CliFlags = { + strict: false, + json: false, + contrastGrid: getDefaultContrastGrid(), + }; + for (let i = 0; i < argv.length; i++) { + const arg = argv[i]; + if (arg === '--strict') flags.strict = true; + else if (arg === '--json') flags.json = true; + else if (arg === '--contrast') { + const raw = argv[++i]; + if (raw) { + flags.contrastGrid = raw + .split(',') + .map((s) => Number(s.trim())) + .filter((n) => Number.isFinite(n)); + } + } else if (arg === '--help' || arg === '-h') { + process.stdout.write( + [ + 'Usage: verify-theme-tokens [options]', + '', + 'Options:', + ' --strict Treat auxiliary contrast warnings as errors', + ' --json Emit JSON report on stdout', + ' --contrast a,b,c Override contrast grid (default: 0,25,43,75,100)', + ' -h, --help Show this help', + '', + ].join('\n') + ); + process.exit(0); + } + } + return flags; +} + +const COLORS = { + reset: '\x1b[0m', + red: '\x1b[31m', + yellow: '\x1b[33m', + green: '\x1b[32m', + dim: '\x1b[2m', + bold: '\x1b[1m', + cyan: '\x1b[36m', +}; + +function colorize(text: string, code: string): string { + if (!process.stdout.isTTY) return text; + return `${code}${text}${COLORS.reset}`; +} + +function groupFindings( + findings: VerifyFinding[] +): Map { + const groups = new Map(); + for (const f of findings) { + const key = `${f.mode} / ${f.themeId} / contrast=${f.contrast}`; + const bucket = groups.get(key); + if (bucket) bucket.push(f); + else groups.set(key, [f]); + } + return groups; +} + +function printHumanReport( + themes: Array<{ mode: string; id: string }>, + flags: CliFlags, + report: ReturnType +): void { + const { summary, findings } = report; + + process.stdout.write( + `\n${colorize('Design Token Engine Verification (V2)', COLORS.bold)}\n` + ); + process.stdout.write( + colorize( + ` Registered themes: ${themes.map((t) => `${t.mode}/${t.id}`).join(', ')}\n`, + COLORS.dim + ) + ); + process.stdout.write( + colorize( + ` Contrast grid: ${flags.contrastGrid.join(', ')}\n`, + COLORS.dim + ) + ); + process.stdout.write( + colorize(` Variants checked: ${summary.variantsChecked}\n\n`, COLORS.dim) + ); + + if (findings.length === 0) { + process.stdout.write( + `${colorize('PASS', COLORS.green)} No findings — engine is clean.\n\n` + ); + return; + } + + const groups = groupFindings(findings); + for (const [variant, bucket] of groups) { + process.stdout.write(`${colorize(variant, COLORS.cyan)}\n`); + for (const f of bucket) { + const badge = + f.severity === 'error' + ? colorize('ERROR', COLORS.red) + : colorize('WARN ', COLORS.yellow); + const ratioSuffix = + f.ratio !== undefined && f.threshold !== undefined + ? colorize( + ` (ratio ${f.ratio.toFixed(2)} / threshold ${f.threshold})`, + COLORS.dim + ) + : ''; + process.stdout.write( + ` ${badge} [${f.code}] ${f.message}${ratioSuffix}\n` + ); + if (f.tokenKey && f.value) { + process.stdout.write( + colorize(` ↳ ${f.tokenKey} = ${f.value}\n`, COLORS.dim) + ); + } + } + process.stdout.write('\n'); + } + + const errBadge = + summary.errors === 0 + ? colorize(`${summary.errors} errors`, COLORS.green) + : colorize(`${summary.errors} errors`, COLORS.red); + const warnBadge = + summary.warnings === 0 + ? colorize(`${summary.warnings} warnings`, COLORS.green) + : colorize(`${summary.warnings} warnings`, COLORS.yellow); + process.stdout.write(`Summary: ${errBadge}, ${warnBadge}\n\n`); +} + +function main() { + const flags = parseArgs(process.argv.slice(2)); + const themes = listRegisteredThemes(); + const report = verifyThemeEngine({ + contrastGrid: flags.contrastGrid, + strictAuxContrast: flags.strict, + }); + + if (flags.json) { + process.stdout.write(JSON.stringify({ themes, ...report }, null, 2) + '\n'); + } else { + printHumanReport(themes, flags, report); + } + + const failed = report.summary.errors > 0; + process.exit(failed ? 1 : 0); +} + +main(); diff --git a/src/components/AddWorker/ToolSelect.tsx b/src/components/AddWorker/ToolSelect.tsx index 4db50bf6..a34065d5 100644 --- a/src/components/AddWorker/ToolSelect.tsx +++ b/src/components/AddWorker/ToolSelect.tsx @@ -758,12 +758,12 @@ const ToolSelect = forwardRef< {(initialSelectedTools || []).map((item: any) => ( {item.name || item.mcp_name || item.key || `tool_${item.id}`} -
+
removeOption(item)} />
@@ -852,7 +852,7 @@ const ToolSelect = forwardRef< >
{/* {getCategoryIcon(item.category?.name)} */} -
+
{item.mcp_name}
@@ -863,10 +863,7 @@ const ToolSelect = forwardRef<
-
@@ -874,7 +871,7 @@ const ToolSelect = forwardRef< ); return (
-
+
{t('workforce.agent-tool')} @@ -889,7 +886,7 @@ const ToolSelect = forwardRef< inputRef.current?.focus(); setIsOpen(true); }} - className="gap-1 rounded-lg border-input-border-default bg-input-bg-default py-1 flex max-h-[120px] min-h-[60px] w-full flex-wrap justify-start overflow-y-auto border border-solid px-[6px]" + className="gap-1 rounded-lg border-ds-border-neutral-default-default bg-ds-bg-neutral-default-default py-1 flex max-h-[120px] min-h-[60px] w-full flex-wrap justify-start overflow-y-auto border border-solid px-[6px]" > {renderSelectedItems()}