mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-13 00:14:15 +00:00
feat(settings): refine browser automation tab
This commit is contained in:
1 parent
4e30c9b080
commit
06f6f5485f
6 files changed
+669
-391
No files matched your search
+10
@@ -8,6 +8,7 @@
|
|||||||
"@hookform/resolvers": "^5.2.2",
|
"@hookform/resolvers": "^5.2.2",
|
||||||
"@nivo/core": "^0.99.0",
|
"@nivo/core": "^0.99.0",
|
||||||
"@nivo/sankey": "^0.99.0",
|
"@nivo/sankey": "^0.99.0",
|
||||||
|
"@phosphor-icons/react": "^2.1.10",
|
||||||
"@radix-ui/react-alert-dialog": "^1.1.15",
|
"@radix-ui/react-alert-dialog": "^1.1.15",
|
||||||
"@radix-ui/react-checkbox": "^1.3.3",
|
"@radix-ui/react-checkbox": "^1.3.3",
|
||||||
"@radix-ui/react-collapsible": "^1.1.12",
|
"@radix-ui/react-collapsible": "^1.1.12",
|
||||||
@@ -28,6 +29,7 @@
|
|||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
|
"framer-motion": "^12.38.0",
|
||||||
"i18next": "^25.8.13",
|
"i18next": "^25.8.13",
|
||||||
"lucide-react": "^0.556.0",
|
"lucide-react": "^0.556.0",
|
||||||
"prism-react-renderer": "^2.4.1",
|
"prism-react-renderer": "^2.4.1",
|
||||||
@@ -273,6 +275,8 @@
|
|||||||
|
|
||||||
"@open-draft/until": ["@open-draft/until@2.1.0", "", {}, "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg=="],
|
"@open-draft/until": ["@open-draft/until@2.1.0", "", {}, "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg=="],
|
||||||
|
|
||||||
|
"@phosphor-icons/react": ["@phosphor-icons/react@2.1.10", "", { "peerDependencies": { "react": ">= 16.8", "react-dom": ">= 16.8" } }, "sha512-vt8Tvq8GLjheAZZYa+YG/pW7HDbov8El/MANW8pOAz4eGxrwhnbfrQZq0Cp4q8zBEu8NIhHdnr+r8thnfRSNYA=="],
|
||||||
|
|
||||||
"@radix-ui/number": ["@radix-ui/number@1.1.1", "", {}, "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g=="],
|
"@radix-ui/number": ["@radix-ui/number@1.1.1", "", {}, "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g=="],
|
||||||
|
|
||||||
"@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="],
|
"@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="],
|
||||||
@@ -735,6 +739,8 @@
|
|||||||
|
|
||||||
"flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="],
|
"flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="],
|
||||||
|
|
||||||
|
"framer-motion": ["framer-motion@12.38.0", "", { "dependencies": { "motion-dom": "^12.38.0", "motion-utils": "^12.36.0", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-rFYkY/pigbcswl1XQSb7q424kSTQ8q6eAC+YUsSKooHQYuLdzdHjrt6uxUC+PRAO++q5IS7+TamgIw1AphxR+g=="],
|
||||||
|
|
||||||
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
||||||
|
|
||||||
"gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="],
|
"gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="],
|
||||||
@@ -877,6 +883,10 @@
|
|||||||
|
|
||||||
"minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
"minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
||||||
|
|
||||||
|
"motion-dom": ["motion-dom@12.38.0", "", { "dependencies": { "motion-utils": "^12.36.0" } }, "sha512-pdkHLD8QYRp8VfiNLb8xIBJis1byQ9gPT3Jnh2jqfFtAsWUA3dEepDlsWe/xMpO8McV+VdpKVcp+E+TGJEtOoA=="],
|
||||||
|
|
||||||
|
"motion-utils": ["motion-utils@12.36.0", "", {}, "sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg=="],
|
||||||
|
|
||||||
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||||
|
|
||||||
"msw": ["msw@2.12.4", "", { "dependencies": { "@inquirer/confirm": "^5.0.0", "@mswjs/interceptors": "^0.40.0", "@open-draft/deferred-promise": "^2.2.0", "@types/statuses": "^2.0.6", "cookie": "^1.0.2", "graphql": "^16.12.0", "headers-polyfill": "^4.0.2", "is-node-process": "^1.2.0", "outvariant": "^1.4.3", "path-to-regexp": "^6.3.0", "picocolors": "^1.1.1", "rettime": "^0.7.0", "statuses": "^2.0.2", "strict-event-emitter": "^0.5.1", "tough-cookie": "^6.0.0", "type-fest": "^5.2.0", "until-async": "^3.0.2", "yargs": "^17.7.2" }, "peerDependencies": { "typescript": ">= 4.8.x" }, "optionalPeers": ["typescript"], "bin": { "msw": "cli/index.js" } }, "sha512-rHNiVfTyKhzc0EjoXUBVGteNKBevdjOlVC6GlIRXpy+/3LHEIGRovnB5WPjcvmNODVQ1TNFnoa7wsGbd0V3epg=="],
|
"msw": ["msw@2.12.4", "", { "dependencies": { "@inquirer/confirm": "^5.0.0", "@mswjs/interceptors": "^0.40.0", "@open-draft/deferred-promise": "^2.2.0", "@types/statuses": "^2.0.6", "cookie": "^1.0.2", "graphql": "^16.12.0", "headers-polyfill": "^4.0.2", "is-node-process": "^1.2.0", "outvariant": "^1.4.3", "path-to-regexp": "^6.3.0", "picocolors": "^1.1.1", "rettime": "^0.7.0", "statuses": "^2.0.2", "strict-event-emitter": "^0.5.1", "tough-cookie": "^6.0.0", "type-fest": "^5.2.0", "until-async": "^3.0.2", "yargs": "^17.7.2" }, "peerDependencies": { "typescript": ">= 4.8.x" }, "optionalPeers": ["typescript"], "bin": { "msw": "cli/index.js" } }, "sha512-rHNiVfTyKhzc0EjoXUBVGteNKBevdjOlVC6GlIRXpy+/3LHEIGRovnB5WPjcvmNODVQ1TNFnoa7wsGbd0V3epg=="],
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
"@hookform/resolvers": "^5.2.2",
|
"@hookform/resolvers": "^5.2.2",
|
||||||
"@nivo/core": "^0.99.0",
|
"@nivo/core": "^0.99.0",
|
||||||
"@nivo/sankey": "^0.99.0",
|
"@nivo/sankey": "^0.99.0",
|
||||||
|
"@phosphor-icons/react": "^2.1.10",
|
||||||
"@radix-ui/react-alert-dialog": "^1.1.15",
|
"@radix-ui/react-alert-dialog": "^1.1.15",
|
||||||
"@radix-ui/react-checkbox": "^1.3.3",
|
"@radix-ui/react-checkbox": "^1.3.3",
|
||||||
"@radix-ui/react-collapsible": "^1.1.12",
|
"@radix-ui/react-collapsible": "^1.1.12",
|
||||||
@@ -42,6 +43,7 @@
|
|||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
|
"framer-motion": "^12.38.0",
|
||||||
"i18next": "^25.8.13",
|
"i18next": "^25.8.13",
|
||||||
"lucide-react": "^0.556.0",
|
"lucide-react": "^0.556.0",
|
||||||
"prism-react-renderer": "^2.4.1",
|
"prism-react-renderer": "^2.4.1",
|
||||||
|
|||||||
@@ -2433,6 +2433,8 @@ const resources = {
|
|||||||
'Configure Claude Browser Attach and Codex Browser Tools here, then use the guidance below to launch or verify each lane.',
|
'Configure Claude Browser Attach and Codex Browser Tools here, then use the guidance below to launch or verify each lane.',
|
||||||
readiness: 'Readiness',
|
readiness: 'Readiness',
|
||||||
nextStep: 'Next step',
|
nextStep: 'Next step',
|
||||||
|
technicalDetails: 'Technical Details',
|
||||||
|
diagnostics: 'Diagnostics',
|
||||||
actions: {
|
actions: {
|
||||||
saveClaude: 'Save Claude settings',
|
saveClaude: 'Save Claude settings',
|
||||||
saveCodex: 'Save Codex settings',
|
saveCodex: 'Save Codex settings',
|
||||||
@@ -4846,6 +4848,8 @@ const resources = {
|
|||||||
'在这里配置 Claude Browser Attach 和 Codex Browser Tools,然后按下方指引启动或验证每条路径。',
|
'在这里配置 Claude Browser Attach 和 Codex Browser Tools,然后按下方指引启动或验证每条路径。',
|
||||||
readiness: '就绪状态',
|
readiness: '就绪状态',
|
||||||
nextStep: '下一步',
|
nextStep: '下一步',
|
||||||
|
technicalDetails: '技术细节',
|
||||||
|
diagnostics: '诊断信息',
|
||||||
actions: {
|
actions: {
|
||||||
saveClaude: '保存 Claude 设置',
|
saveClaude: '保存 Claude 设置',
|
||||||
saveCodex: '保存 Codex 设置',
|
saveCodex: '保存 Codex 设置',
|
||||||
@@ -7361,6 +7365,8 @@ const resources = {
|
|||||||
'Cấu hình Claude Browser Attach và Codex Browser Tools tại đây, rồi dùng hướng dẫn bên dưới để khởi chạy hoặc kiểm tra từng luồng.',
|
'Cấu hình Claude Browser Attach và Codex Browser Tools tại đây, rồi dùng hướng dẫn bên dưới để khởi chạy hoặc kiểm tra từng luồng.',
|
||||||
readiness: 'Trạng thái sẵn sàng',
|
readiness: 'Trạng thái sẵn sàng',
|
||||||
nextStep: 'Bước tiếp theo',
|
nextStep: 'Bước tiếp theo',
|
||||||
|
technicalDetails: 'Chi tiết kỹ thuật',
|
||||||
|
diagnostics: 'Chẩn đoán',
|
||||||
actions: {
|
actions: {
|
||||||
saveClaude: 'Lưu cấu hình Claude',
|
saveClaude: 'Lưu cấu hình Claude',
|
||||||
saveCodex: 'Lưu cấu hình Codex',
|
saveCodex: 'Lưu cấu hình Codex',
|
||||||
@@ -9783,6 +9789,8 @@ const resources = {
|
|||||||
'ここで Claude Browser Attach と Codex Browser Tools を設定し、各レーンの起動や確認は下の案内を使ってください。',
|
'ここで Claude Browser Attach と Codex Browser Tools を設定し、各レーンの起動や確認は下の案内を使ってください。',
|
||||||
readiness: '準備状況',
|
readiness: '準備状況',
|
||||||
nextStep: '次の手順',
|
nextStep: '次の手順',
|
||||||
|
technicalDetails: '技術的な詳細',
|
||||||
|
diagnostics: '診断情報',
|
||||||
actions: {
|
actions: {
|
||||||
saveClaude: 'Claude 設定を保存',
|
saveClaude: 'Claude 設定を保存',
|
||||||
saveCodex: 'Codex 設定を保存',
|
saveCodex: 'Codex 設定を保存',
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
export type ClientPlatformKey = 'darwin' | 'linux' | 'win32';
|
||||||
|
|
||||||
|
type NavigatorWithUserAgentData = Pick<Navigator, 'platform' | 'userAgent'> & {
|
||||||
|
userAgentData?: {
|
||||||
|
platform?: string | null;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
function normalizeClientPlatform(value: string): ClientPlatformKey {
|
||||||
|
const platform = value.toLowerCase();
|
||||||
|
if (platform.includes('mac')) return 'darwin';
|
||||||
|
if (platform.includes('win')) return 'win32';
|
||||||
|
return 'linux';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getClientPlatformKey(
|
||||||
|
nav: NavigatorWithUserAgentData = navigator as NavigatorWithUserAgentData
|
||||||
|
): ClientPlatformKey {
|
||||||
|
const userAgentDataPlatform =
|
||||||
|
typeof nav.userAgentData?.platform === 'string' ? nav.userAgentData.platform : '';
|
||||||
|
const fallbackPlatform =
|
||||||
|
typeof nav.platform === 'string' && nav.platform.trim() ? nav.platform : nav.userAgent;
|
||||||
|
|
||||||
|
return normalizeClientPlatform(userAgentDataPlatform || fallbackPlatform || '');
|
||||||
|
}
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,32 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import { getClientPlatformKey } from '@/lib/platform';
|
||||||
|
|
||||||
|
describe('getClientPlatformKey', () => {
|
||||||
|
it('prefers navigator.userAgentData.platform when available', () => {
|
||||||
|
expect(
|
||||||
|
getClientPlatformKey({
|
||||||
|
userAgentData: { platform: 'macOS' },
|
||||||
|
platform: 'Win32',
|
||||||
|
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)',
|
||||||
|
})
|
||||||
|
).toBe('darwin');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('falls back to navigator.platform when userAgentData is unavailable', () => {
|
||||||
|
expect(
|
||||||
|
getClientPlatformKey({
|
||||||
|
platform: 'Win32',
|
||||||
|
userAgent: 'Mozilla/5.0 (X11; Linux x86_64)',
|
||||||
|
})
|
||||||
|
).toBe('win32');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('falls back to user-agent parsing when platform is unavailable', () => {
|
||||||
|
expect(
|
||||||
|
getClientPlatformKey({
|
||||||
|
platform: '',
|
||||||
|
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)',
|
||||||
|
})
|
||||||
|
).toBe('win32');
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in new issue
Block a user