From 612c6610ce1fb50a43e1ce1c11e0385e971ac208 Mon Sep 17 00:00:00 2001 From: zhom <2717306+zhom@users.noreply.github.com> Date: Sat, 28 Mar 2026 23:31:20 +0400 Subject: [PATCH] chore: linting --- .claude/skills/ui-ux-pro-max/SKILL.md | 5 ++ CONTRIBUTING.md | 2 + donut-sync/README.md | 20 ++++---- donut-sync/src/auth/auth.guard.ts | 4 +- donut-sync/src/sync/sync.controller.ts | 2 +- src/app/page.tsx | 10 ++-- src/components/cookie-copy-dialog.tsx | 20 ++++---- src/components/cookie-management-dialog.tsx | 18 +++---- src/components/create-profile-dialog.tsx | 26 +++++----- src/components/custom-toast.tsx | 10 ++-- src/components/data-table-action-bar.tsx | 2 +- src/components/group-assignment-dialog.tsx | 2 +- src/components/multiple-selector.tsx | 6 +-- src/components/profile-data-table.tsx | 18 +++++-- src/components/profile-info-dialog.tsx | 2 +- src/components/settings-dialog.tsx | 23 ++++----- .../shared-camoufox-config-form.tsx | 12 ++--- src/components/sync-follower-dialog.tsx | 4 +- src/components/traffic-details-dialog.tsx | 4 +- src/components/ui/color-picker.tsx | 2 +- src/components/ui/highlight.tsx | 4 +- src/components/wayfern-config-form.tsx | 2 +- src/hooks/use-auto-height.tsx | 19 +++----- src/hooks/use-browser-download.ts | 4 +- src/hooks/use-cloud-auth.ts | 2 +- src/hooks/use-controlled-state.tsx | 2 +- src/hooks/use-proxy-events.ts | 2 +- src/hooks/use-vpn-events.ts | 2 +- src/i18n/locales/en.json | 48 +++++++++---------- src/i18n/locales/es.json | 48 +++++++++---------- src/i18n/locales/fr.json | 48 +++++++++---------- src/i18n/locales/ja.json | 48 +++++++++---------- src/i18n/locales/pt.json | 48 +++++++++---------- src/i18n/locales/ru.json | 48 +++++++++---------- src/i18n/locales/zh.json | 48 +++++++++---------- src/types.ts | 4 +- 36 files changed, 284 insertions(+), 285 deletions(-) diff --git a/.claude/skills/ui-ux-pro-max/SKILL.md b/.claude/skills/ui-ux-pro-max/SKILL.md index a937fb2..4b2ec8f 100644 --- a/.claude/skills/ui-ux-pro-max/SKILL.md +++ b/.claude/skills/ui-ux-pro-max/SKILL.md @@ -197,6 +197,7 @@ These are frequently overlooked issues that make UI look unprofessional: Before delivering UI code, verify these items: ### Visual Quality + - [ ] No emojis used as icons (use SVG instead) - [ ] All icons from consistent icon set (Heroicons/Lucide) - [ ] Brand logos are correct (verified from Simple Icons) @@ -204,24 +205,28 @@ Before delivering UI code, verify these items: - [ ] Use theme colors directly (bg-primary) not var() wrapper ### Interaction + - [ ] All clickable elements have `cursor-pointer` - [ ] Hover states provide clear visual feedback - [ ] Transitions are smooth (150-300ms) - [ ] Focus states visible for keyboard navigation ### Light/Dark Mode + - [ ] Light mode text has sufficient contrast (4.5:1 minimum) - [ ] Glass/transparent elements visible in light mode - [ ] Borders visible in both modes - [ ] Test both modes before delivery ### Layout + - [ ] Floating elements have proper spacing from edges - [ ] No content hidden behind fixed navbars - [ ] Responsive at 320px, 768px, 1024px, 1440px - [ ] No horizontal scroll on mobile ### Accessibility + - [ ] All images have alt text - [ ] Form inputs have labels - [ ] Color is not the only indicator diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ad05943..6659250 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -27,6 +27,7 @@ Or enter the dev shell: `nix develop` ### Manual Setup Requirements: + - Node.js (see `.node-version`) - pnpm - Rust + Cargo (latest stable) @@ -47,6 +48,7 @@ pnpm format && pnpm lint && pnpm test ``` This runs: + - **Biome** — JS/TS linting and formatting - **Clippy + rustfmt** — Rust linting and formatting - **typos** — Spellcheck (allowlist in `_typos.toml`) diff --git a/donut-sync/README.md b/donut-sync/README.md index d30c946..bb15dfc 100644 --- a/donut-sync/README.md +++ b/donut-sync/README.md @@ -2,8 +2,6 @@ Nest Logo

-[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456 -[circleci-url]: https://circleci.com/gh/nestjs/nest

A progressive Node.js framework for building efficient and scalable server-side applications.

@@ -28,33 +26,33 @@ ## Project setup ```bash -$ pnpm install +pnpm install ``` ## Compile and run the project ```bash # development -$ pnpm run start +pnpm run start # watch mode -$ pnpm run start:dev +pnpm run start:dev # production mode -$ pnpm run start:prod +pnpm run start:prod ``` ## Run tests ```bash # unit tests -$ pnpm run test +pnpm run test # e2e tests -$ pnpm run test:e2e +pnpm run test:e2e # test coverage -$ pnpm run test:cov +pnpm run test:cov ``` ## Deployment @@ -64,8 +62,8 @@ When you're ready to deploy your NestJS application to production, there are som If you are looking for a cloud-based platform to deploy your NestJS application, check out [Mau](https://mau.nestjs.com), our official platform for deploying NestJS applications on AWS. Mau makes deployment straightforward and fast, requiring just a few simple steps: ```bash -$ pnpm install -g @nestjs/mau -$ mau deploy +pnpm install -g @nestjs/mau +mau deploy ``` With Mau, you can deploy your application in just a few clicks, allowing you to focus on building features rather than managing infrastructure. diff --git a/donut-sync/src/auth/auth.guard.ts b/donut-sync/src/auth/auth.guard.ts index 3053527..e23088b 100644 --- a/donut-sync/src/auth/auth.guard.ts +++ b/donut-sync/src/auth/auth.guard.ts @@ -38,7 +38,7 @@ export class AuthGuard implements CanActivate { // Try SYNC_TOKEN first (self-hosted mode) const expectedToken = this.configService.get("SYNC_TOKEN"); if (expectedToken && token === expectedToken) { - (request as any).user = { + (request as unknown as Record).user = { mode: "self-hosted", prefix: "", teamPrefix: null, @@ -55,7 +55,7 @@ export class AuthGuard implements CanActivate { algorithms: ["RS256"], }) as jwt.JwtPayload; - (request as any).user = { + (request as unknown as Record).user = { mode: "cloud", prefix: decoded.prefix || `users/${decoded.sub}/`, teamPrefix: decoded.teamPrefix || null, diff --git a/donut-sync/src/sync/sync.controller.ts b/donut-sync/src/sync/sync.controller.ts index 76df58c..1be8ad6 100644 --- a/donut-sync/src/sync/sync.controller.ts +++ b/donut-sync/src/sync/sync.controller.ts @@ -39,7 +39,7 @@ export class SyncController { constructor(private readonly syncService: SyncService) {} private getUserContext(req: Request): UserContext { - return (req as any).user as UserContext; + return (req as unknown as Record).user as UserContext; } @Post("stat") diff --git a/src/app/page.tsx b/src/app/page.tsx index 83a8875..5130abe 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -324,7 +324,7 @@ export default function Home() { const currentUrl = await getCurrent(); if (currentUrl && currentUrl.length > 0) { console.log("Startup URL detected:", currentUrl[0]); - void handleUrlOpen(currentUrl[0]); + handleUrlOpen(currentUrl[0]); } } catch (error) { console.error("Failed to check current URL:", error); @@ -413,13 +413,13 @@ export default function Home() { // Listen for URL open events from the deep link handler (when app is already running) await listen("url-open-request", (event) => { console.log("Received URL open request:", event.payload); - void handleUrlOpen(event.payload); + handleUrlOpen(event.payload); }); // Listen for show profile selector events await listen("show-profile-selector", (event) => { console.log("Received show profile selector request:", event.payload); - void handleUrlOpen(event.payload); + handleUrlOpen(event.payload); }); // Listen for show create profile dialog events @@ -437,7 +437,7 @@ export default function Home() { // Listen for custom logo click events const handleLogoUrlEvent = (event: CustomEvent) => { console.log("Received logo URL event:", event.detail); - void handleUrlOpen(event.detail); + handleUrlOpen(event.detail); }; window.addEventListener( @@ -995,7 +995,7 @@ export default function Home() { // Check permissions when they are initialized useEffect(() => { if (isInitialized) { - void checkAllPermissions(); + checkAllPermissions(); } }, [isInitialized, checkAllPermissions]); diff --git a/src/components/cookie-copy-dialog.tsx b/src/components/cookie-copy-dialog.tsx index 0fa16fd..2532032 100644 --- a/src/components/cookie-copy-dialog.tsx +++ b/src/components/cookie-copy-dialog.tsx @@ -50,12 +50,13 @@ interface CookieCopyDialogProps { onCopyComplete?: () => void; } -interface SelectionState { - [domain: string]: { +type SelectionState = Record< + string, + { allSelected: boolean; cookies: Set; - }; -} + } +>; export function CookieCopyDialog({ isOpen, @@ -148,7 +149,7 @@ export function CookieCopyDialog({ (domain: string, cookies: UnifiedCookie[]) => { setSelection((prev) => { const current = prev[domain]; - const allSelected = current.allSelected || false; + const allSelected = current.allSelected; if (allSelected) { const newSelection = { ...prev }; @@ -171,7 +172,7 @@ export function CookieCopyDialog({ const toggleCookie = useCallback( (domain: string, cookieName: string, totalCookies: number) => { setSelection((prev) => { - const current = prev[domain] || { + const current = prev[domain] ?? { allSelected: false, cookies: new Set(), }; @@ -503,8 +504,8 @@ function DomainRow({ onToggleExpand, }: DomainRowProps) { const domainSelection = selection[domain.domain]; - const isAllSelected = domainSelection.allSelected || false; - const selectedCount = domainSelection.cookies.size || 0; + const isAllSelected = domainSelection.allSelected; + const selectedCount = domainSelection.cookies.size; const isPartial = selectedCount > 0 && selectedCount < domain.cookie_count && !isAllSelected; @@ -539,8 +540,7 @@ function DomainRow({ {isExpanded && (

{domain.cookies.map((cookie) => { - const isSelected = - domainSelection.cookies.has(cookie.name) || false; + const isSelected = domainSelection.cookies.has(cookie.name); return (
; - }; -} + } +>; const countCookies = (content: string): number => { const trimmed = content.trim(); @@ -329,7 +330,7 @@ export function CookieManagementDialog({ const toggleCookie = useCallback( (domain: string, cookieName: string, totalCookies: number) => { setExportSelection((prev) => { - const current = prev[domain] || { + const current = prev[domain] ?? { allSelected: false, cookies: new Set(), }; @@ -591,8 +592,8 @@ function ExportDomainRow({ onToggleExpand, }: ExportDomainRowProps) { const domainSelection = selection[domain.domain]; - const isAllSelected = domainSelection.allSelected || false; - const selectedCount = domainSelection.cookies.size || 0; + const isAllSelected = domainSelection.allSelected; + const selectedCount = domainSelection.cookies.size; const isPartial = selectedCount > 0 && selectedCount < domain.cookie_count && !isAllSelected; @@ -627,8 +628,7 @@ function ExportDomainRow({ {isExpanded && (
{domain.cookies.map((cookie) => { - const isSelected = - domainSelection.cookies.has(cookie.name) || false; + const isSelected = domainSelection.cookies.has(cookie.name); return (
handleDownload("wayfern")} + onClick={() => { + void handleDownload("wayfern"); + }} isLoading={isBrowserCurrentlyDownloading( "wayfern", )} @@ -856,7 +858,9 @@ export function CreateProfileDialog({ })()}

handleDownload("camoufox")} + onClick={() => { + void handleDownload("camoufox"); + }} isLoading={isBrowserCurrentlyDownloading( "camoufox", )} @@ -963,9 +967,9 @@ export function CreateProfileDialog({ })()}

- handleDownload(selectedBrowser) - } + onClick={() => { + void handleDownload(selectedBrowser); + }} isLoading={isBrowserCurrentlyDownloading( selectedBrowser, )} @@ -1163,7 +1167,7 @@ export function CreateProfileDialog({
{ setSelectedGroupId(value === "default" ? null : value); }} diff --git a/src/components/multiple-selector.tsx b/src/components/multiple-selector.tsx index 541f13b..ede4f91 100644 --- a/src/components/multiple-selector.tsx +++ b/src/components/multiple-selector.tsx @@ -19,9 +19,7 @@ export interface Option { /** Group the options by providing key. */ [key: string]: string | boolean | undefined; } -interface GroupOption { - [key: string]: Option[]; -} +type GroupOption = Record; interface MultipleSelectorProps { value?: Option[]; @@ -259,7 +257,7 @@ const MultipleSelector = React.forwardRef< if (!arrayOptions || onSearch) { return; } - const newOption = transToGroupOption(arrayOptions || [], groupBy); + const newOption = transToGroupOption(arrayOptions, groupBy); if (JSON.stringify(newOption) !== JSON.stringify(options)) { setOptions(newOption); } diff --git a/src/components/profile-data-table.tsx b/src/components/profile-data-table.tsx index 9c66e12..13679e1 100644 --- a/src/components/profile-data-table.tsx +++ b/src/components/profile-data-table.tsx @@ -218,12 +218,12 @@ interface TableMeta { onLaunchWithSync: (profile: BrowserProfile) => void; } -type SyncStatusDot = { +interface SyncStatusDot { color: string; tooltip: string; animate: boolean; encrypted: boolean; -}; +} function getProfileSyncStatusDot( profile: BrowserProfile, @@ -1215,7 +1215,7 @@ export function ProfilesDataTable({ React.useEffect(() => { if (!browserState.isClient) return; let unlisten: (() => void) | undefined; - (async () => { + void (async () => { try { unlisten = await listen<{ id: string; is_running: boolean }>( "profile-running-changed", @@ -1540,7 +1540,11 @@ export function ProfilesDataTable({ // Overflow actions onAssignProfilesToGroup, - onCloneProfile, + onCloneProfile: onCloneProfile + ? (profile: BrowserProfile) => { + void onCloneProfile(profile); + } + : undefined, onConfigureCamoufox, onCopyCookiesToProfile, onOpenCookieManagement, @@ -1572,7 +1576,11 @@ export function ProfilesDataTable({ // Synchronizer getProfileSyncInfo: getProfileSyncInfo ?? (() => undefined), - onLaunchWithSync: onLaunchWithSync ?? (() => {}), + onLaunchWithSync: + onLaunchWithSync ?? + (() => { + /* empty */ + }), }), [ t, diff --git a/src/components/profile-info-dialog.tsx b/src/components/profile-info-dialog.tsx index 93e0579..119bfc2 100644 --- a/src/components/profile-info-dialog.tsx +++ b/src/components/profile-info-dialog.tsx @@ -147,7 +147,7 @@ export function ProfileInfoDialog({ setExtensionGroupName(null); return; } - (async () => { + void (async () => { try { const group = await invoke<{ name: string } | null>( "get_extension_group_for_profile", diff --git a/src/components/settings-dialog.tsx b/src/components/settings-dialog.tsx index 2bcbf76..02c7512 100644 --- a/src/components/settings-dialog.tsx +++ b/src/components/settings-dialog.tsx @@ -242,9 +242,9 @@ export function SettingsDialog({ const clearCustomTheme = useCallback(() => { const root = document.documentElement; - THEME_VARIABLES.forEach(({ key }) => - root.style.removeProperty(key as string), - ); + THEME_VARIABLES.forEach(({ key }) => { + root.style.removeProperty(key as string); + }); }, []); const loadPermissions = useCallback(() => { @@ -378,16 +378,13 @@ export function SettingsDialog({ // Apply or clear custom variables only on Save if (settings.theme === "custom") { - if ( - customThemeState.colors && - Object.keys(customThemeState.colors).length > 0 - ) { + if (Object.keys(customThemeState.colors).length > 0) { try { const root = document.documentElement; // Clear any previous custom vars first - THEME_VARIABLES.forEach(({ key }) => - root.style.removeProperty(key as string), - ); + THEME_VARIABLES.forEach(({ key }) => { + root.style.removeProperty(key as string); + }); Object.entries(customThemeState.colors).forEach(([k, v]) => { root.style.setProperty(k, v, "important"); }); @@ -398,9 +395,9 @@ export function SettingsDialog({ } else { try { const root = document.documentElement; - THEME_VARIABLES.forEach(({ key }) => - root.style.removeProperty(key as string), - ); + THEME_VARIABLES.forEach(({ key }) => { + root.style.removeProperty(key as string); + }); } catch { /* empty */ } diff --git a/src/components/shared-camoufox-config-form.tsx b/src/components/shared-camoufox-config-form.tsx index b94d199..cc46f65 100644 --- a/src/components/shared-camoufox-config-form.tsx +++ b/src/components/shared-camoufox-config-form.tsx @@ -77,7 +77,7 @@ function ObjectEditor({ const [jsonString, setJsonString] = useState(""); useEffect(() => { - setJsonString(JSON.stringify(value || {}, null, 2)); + setJsonString(JSON.stringify(value ?? {}, null, 2)); }, [value]); const handleChange = (newValue: string) => { @@ -144,7 +144,7 @@ export function SharedCamoufoxConfigForm({ const handleGenerateFingerprint = async () => { if (!profileVersion) return; - const browser = profileBrowser || browserType || "camoufox"; + const browser = profileBrowser ?? browserType ?? "camoufox"; setIsGeneratingFingerprint(true); try { const configJson = JSON.stringify(config); @@ -917,7 +917,7 @@ export function SharedCamoufoxConfigForm({ (fingerprintConfig["webGl:parameters"] as Record< string, unknown - >) || {} + >) ?? {} } onChange={(value) => { updateFingerprintConfig("webGl:parameters", value); @@ -934,7 +934,7 @@ export function SharedCamoufoxConfigForm({ (fingerprintConfig["webGl2:parameters"] as Record< string, unknown - >) || {} + >) ?? {} } onChange={(value) => { updateFingerprintConfig("webGl2:parameters", value); @@ -951,7 +951,7 @@ export function SharedCamoufoxConfigForm({ (fingerprintConfig["webGl:shaderPrecisionFormats"] as Record< string, unknown - >) || {} + >) ?? {} } onChange={(value) => { updateFingerprintConfig("webGl:shaderPrecisionFormats", value); @@ -968,7 +968,7 @@ export function SharedCamoufoxConfigForm({ (fingerprintConfig["webGl2:shaderPrecisionFormats"] as Record< string, unknown - >) || {} + >) ?? {} } onChange={(value) => { updateFingerprintConfig("webGl2:shaderPrecisionFormats", value); diff --git a/src/components/sync-follower-dialog.tsx b/src/components/sync-follower-dialog.tsx index aa2822b..e2edb87 100644 --- a/src/components/sync-follower-dialog.tsx +++ b/src/components/sync-follower-dialog.tsx @@ -162,7 +162,9 @@ export function SyncFollowerDialog({ !selectedIds.has(profile.id), ); }} - onKeyDown={() => {}} + onKeyDown={() => { + /* empty */ + }} role="button" tabIndex={0} > diff --git a/src/components/traffic-details-dialog.tsx b/src/components/traffic-details-dialog.tsx index 04a89f6..6a1dcb1 100644 --- a/src/components/traffic-details-dialog.tsx +++ b/src/components/traffic-details-dialog.tsx @@ -173,7 +173,9 @@ export function TrafficDetailsDialog({ }; void fetchStats(); - const interval = setInterval(fetchStats, 2000); + const interval = setInterval(() => { + void fetchStats(); + }, 2000); return () => { clearInterval(interval); diff --git a/src/components/ui/color-picker.tsx b/src/components/ui/color-picker.tsx index b39bfc0..2e8dee5 100644 --- a/src/components/ui/color-picker.tsx +++ b/src/components/ui/color-picker.tsx @@ -363,7 +363,7 @@ export type ColorPickerOutputProps = ComponentProps; const formats = ["hex", "rgb", "css", "hsl"]; export const ColorPickerOutput = ({ - className, + className: _className, ...props }: ColorPickerOutputProps) => { const { mode, setMode } = useColorPicker(); diff --git a/src/components/ui/highlight.tsx b/src/components/ui/highlight.tsx index 2e81204..ed4389e 100644 --- a/src/components/ui/highlight.tsx +++ b/src/components/ui/highlight.tsx @@ -43,7 +43,7 @@ interface HighlightContextType { } const HighlightContext = React.createContext< - HighlightContextType | undefined + HighlightContextType | undefined >(undefined); function useHighlight(): HighlightContextType { @@ -419,7 +419,7 @@ function HighlightItem({ const Component = as ?? "div"; const element = children as React.ReactElement; const childValue = - id ?? value ?? element.props?.["data-value"] ?? element.props?.id ?? itemId; + id ?? value ?? element.props["data-value"] ?? element.props.id ?? itemId; const isActive = activeValue === childValue; const isDisabled = disabled === undefined ? contextDisabled : disabled; const itemTransition = transition ?? contextTransition; diff --git a/src/components/wayfern-config-form.tsx b/src/components/wayfern-config-form.tsx index 328f5d7..9a52416 100644 --- a/src/components/wayfern-config-form.tsx +++ b/src/components/wayfern-config-form.tsx @@ -84,7 +84,7 @@ export function WayfernConfigForm({ try { const configJson = JSON.stringify(config); const result = await invoke("generate_sample_fingerprint", { - browser: profileBrowser || "wayfern", + browser: profileBrowser ?? "wayfern", version: profileVersion, configJson, }); diff --git a/src/hooks/use-auto-height.tsx b/src/hooks/use-auto-height.tsx index 8a65a3e..2867e7e 100644 --- a/src/hooks/use-auto-height.tsx +++ b/src/hooks/use-auto-height.tsx @@ -22,18 +22,15 @@ export function useAutoHeight( const el = ref.current; if (!el) return 0; - const base = el.getBoundingClientRect().height ?? 0; + const base = el.getBoundingClientRect().height; let extra = 0; if (options.includeParentBox && el.parentElement) { const cs = getComputedStyle(el.parentElement); - const paddingY = - (parseFloat(cs.paddingTop ?? "0") ?? 0) + - (parseFloat(cs.paddingBottom ?? "0") ?? 0); + const paddingY = parseFloat(cs.paddingTop) + parseFloat(cs.paddingBottom); const borderY = - (parseFloat(cs.borderTopWidth ?? "0") ?? 0) + - (parseFloat(cs.borderBottomWidth ?? "0") ?? 0); + parseFloat(cs.borderTopWidth) + parseFloat(cs.borderBottomWidth); const isBorderBox = cs.boxSizing === "border-box"; if (isBorderBox) { extra += paddingY + borderY; @@ -42,20 +39,16 @@ export function useAutoHeight( if (options.includeSelfBox) { const cs = getComputedStyle(el); - const paddingY = - (parseFloat(cs.paddingTop ?? "0") ?? 0) + - (parseFloat(cs.paddingBottom ?? "0") ?? 0); + const paddingY = parseFloat(cs.paddingTop) + parseFloat(cs.paddingBottom); const borderY = - (parseFloat(cs.borderTopWidth ?? "0") ?? 0) + - (parseFloat(cs.borderBottomWidth ?? "0") ?? 0); + parseFloat(cs.borderTopWidth) + parseFloat(cs.borderBottomWidth); const isBorderBox = cs.boxSizing === "border-box"; if (isBorderBox) { extra += paddingY + borderY; } } - const dpr = - typeof window !== "undefined" ? (window.devicePixelRatio ?? 1) : 1; + const dpr = typeof window !== "undefined" ? window.devicePixelRatio : 1; const total = Math.ceil((base + extra) * dpr) / dpr; return total; diff --git a/src/hooks/use-browser-download.ts b/src/hooks/use-browser-download.ts index fe1c972..455944a 100644 --- a/src/hooks/use-browser-download.ts +++ b/src/hooks/use-browser-download.ts @@ -367,7 +367,9 @@ export function useBrowserDownload() { ? loadDownloadedVersions("camoufox") : Promise.resolve([]), ]); - } catch {} + } catch { + /* empty */ + } showDownloadToast(browserName, progress.version, "completed"); setDownloadProgress(null); } diff --git a/src/hooks/use-cloud-auth.ts b/src/hooks/use-cloud-auth.ts index aa3d281..a0deb76 100644 --- a/src/hooks/use-cloud-auth.ts +++ b/src/hooks/use-cloud-auth.ts @@ -50,7 +50,7 @@ export function useCloudAuth(): UseCloudAuthReturn { }; }, [loadUser]); - const requestOtp = useCallback(async (email: string): Promise => { + const requestOtp = useCallback((email: string): Promise => { return invoke("cloud_request_otp", { email }); }, []); diff --git a/src/hooks/use-controlled-state.tsx b/src/hooks/use-controlled-state.tsx index bc9fe6b..6903781 100644 --- a/src/hooks/use-controlled-state.tsx +++ b/src/hooks/use-controlled-state.tsx @@ -5,7 +5,7 @@ interface CommonControlledStateProps { defaultValue?: T; } -export function useControlledState( +export function useControlledState( props: CommonControlledStateProps & { onChange?: (value: T, ...args: Rest) => void; }, diff --git a/src/hooks/use-proxy-events.ts b/src/hooks/use-proxy-events.ts index 43d06f2..14c5002 100644 --- a/src/hooks/use-proxy-events.ts +++ b/src/hooks/use-proxy-events.ts @@ -17,7 +17,7 @@ export function useProxyEvents() { // Load proxy usage (how many profiles are using each proxy) const loadProxyUsage = useCallback(async () => { try { - const profiles = await invoke>( + const profiles = await invoke<{ proxy_id?: string }[]>( "list_browser_profiles", ); const counts: Record = {}; diff --git a/src/hooks/use-vpn-events.ts b/src/hooks/use-vpn-events.ts index 1b701c7..afc21e6 100644 --- a/src/hooks/use-vpn-events.ts +++ b/src/hooks/use-vpn-events.ts @@ -16,7 +16,7 @@ export function useVpnEvents() { const loadVpnUsage = useCallback(async () => { try { - const profiles = await invoke>( + const profiles = await invoke<{ vpn_id?: string }[]>( "list_browser_profiles", ); const counts: Record = {}; diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index ab8e3d1..246a58e 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -524,7 +524,29 @@ "selectedCount_plural": "{{count}} cookies selected" }, "success": "Cookies copied successfully", - "error": "Failed to copy cookies" + "error": "Failed to copy cookies", + "management": { + "title": "Cookie Management", + "menuItem": "Cookie Management" + }, + "import": { + "title": "Import Cookies", + "description": "Import cookies from a Netscape or JSON format file.", + "selectFile": "Choose File", + "preview": "{{count}} cookies found", + "success": "Successfully imported {{imported}} cookies ({{replaced}} replaced)", + "error": "Failed to import cookies", + "proFeature": "Cookie import is a Pro feature" + }, + "export": { + "title": "Export Cookies", + "description": "Export cookies from this profile.", + "formatLabel": "Format", + "netscape": "Netscape TXT", + "json": "JSON", + "success": "Cookies exported successfully", + "error": "Failed to export cookies" + } }, "toasts": { "success": { @@ -724,30 +746,6 @@ "cannotLaunch": "This profile was created on {{os}} and is not supported on this system", "cannotModify": "Cannot modify sync settings for a cross-OS profile" }, - "cookies": { - "management": { - "title": "Cookie Management", - "menuItem": "Cookie Management" - }, - "import": { - "title": "Import Cookies", - "description": "Import cookies from a Netscape or JSON format file.", - "selectFile": "Choose File", - "preview": "{{count}} cookies found", - "success": "Successfully imported {{imported}} cookies ({{replaced}} replaced)", - "error": "Failed to import cookies", - "proFeature": "Cookie import is a Pro feature" - }, - "export": { - "title": "Export Cookies", - "description": "Export cookies from this profile.", - "formatLabel": "Format", - "netscape": "Netscape TXT", - "json": "JSON", - "success": "Cookies exported successfully", - "error": "Failed to export cookies" - } - }, "profileInfo": { "title": "Profile Details", "tabs": { diff --git a/src/i18n/locales/es.json b/src/i18n/locales/es.json index f158932..3cd58f8 100644 --- a/src/i18n/locales/es.json +++ b/src/i18n/locales/es.json @@ -524,7 +524,29 @@ "selectedCount_plural": "{{count}} cookies seleccionadas" }, "success": "Cookies copiadas exitosamente", - "error": "Error al copiar cookies" + "error": "Error al copiar cookies", + "management": { + "title": "Gestión de Cookies", + "menuItem": "Gestión de Cookies" + }, + "import": { + "title": "Importar Cookies", + "description": "Importar cookies desde un archivo en formato Netscape o JSON.", + "selectFile": "Elegir Archivo", + "preview": "{{count}} cookies encontradas", + "success": "Se importaron {{imported}} cookies exitosamente ({{replaced}} reemplazadas)", + "error": "Error al importar cookies", + "proFeature": "La importación de cookies es una función Pro" + }, + "export": { + "title": "Exportar Cookies", + "description": "Exportar cookies de este perfil.", + "formatLabel": "Formato", + "netscape": "Netscape TXT", + "json": "JSON", + "success": "Cookies exportadas exitosamente", + "error": "Error al exportar cookies" + } }, "toasts": { "success": { @@ -724,30 +746,6 @@ "cannotLaunch": "Este perfil fue creado en {{os}} y no es compatible con este sistema", "cannotModify": "No se pueden modificar los ajustes de sincronización de un perfil de otro sistema operativo" }, - "cookies": { - "management": { - "title": "Gestión de Cookies", - "menuItem": "Gestión de Cookies" - }, - "import": { - "title": "Importar Cookies", - "description": "Importar cookies desde un archivo en formato Netscape o JSON.", - "selectFile": "Elegir Archivo", - "preview": "{{count}} cookies encontradas", - "success": "Se importaron {{imported}} cookies exitosamente ({{replaced}} reemplazadas)", - "error": "Error al importar cookies", - "proFeature": "La importación de cookies es una función Pro" - }, - "export": { - "title": "Exportar Cookies", - "description": "Exportar cookies de este perfil.", - "formatLabel": "Formato", - "netscape": "Netscape TXT", - "json": "JSON", - "success": "Cookies exportadas exitosamente", - "error": "Error al exportar cookies" - } - }, "profileInfo": { "title": "Detalles del Perfil", "tabs": { diff --git a/src/i18n/locales/fr.json b/src/i18n/locales/fr.json index 5ffd92b..34f58c2 100644 --- a/src/i18n/locales/fr.json +++ b/src/i18n/locales/fr.json @@ -524,7 +524,29 @@ "selectedCount_plural": "{{count}} cookies sélectionnés" }, "success": "Cookies copiés avec succès", - "error": "Échec de la copie des cookies" + "error": "Échec de la copie des cookies", + "management": { + "title": "Gestion des Cookies", + "menuItem": "Gestion des Cookies" + }, + "import": { + "title": "Importer des Cookies", + "description": "Importer des cookies depuis un fichier au format Netscape ou JSON.", + "selectFile": "Choisir un Fichier", + "preview": "{{count}} cookies trouvés", + "success": "{{imported}} cookies importés avec succès ({{replaced}} remplacés)", + "error": "Échec de l'importation des cookies", + "proFeature": "L'importation de cookies est une fonctionnalité Pro" + }, + "export": { + "title": "Exporter les Cookies", + "description": "Exporter les cookies de ce profil.", + "formatLabel": "Format", + "netscape": "Netscape TXT", + "json": "JSON", + "success": "Cookies exportés avec succès", + "error": "Échec de l'exportation des cookies" + } }, "toasts": { "success": { @@ -724,30 +746,6 @@ "cannotLaunch": "Ce profil a été créé sur {{os}} et n'est pas pris en charge sur ce système", "cannotModify": "Impossible de modifier les paramètres de synchronisation d'un profil d'un autre système d'exploitation" }, - "cookies": { - "management": { - "title": "Gestion des Cookies", - "menuItem": "Gestion des Cookies" - }, - "import": { - "title": "Importer des Cookies", - "description": "Importer des cookies depuis un fichier au format Netscape ou JSON.", - "selectFile": "Choisir un Fichier", - "preview": "{{count}} cookies trouvés", - "success": "{{imported}} cookies importés avec succès ({{replaced}} remplacés)", - "error": "Échec de l'importation des cookies", - "proFeature": "L'importation de cookies est une fonctionnalité Pro" - }, - "export": { - "title": "Exporter les Cookies", - "description": "Exporter les cookies de ce profil.", - "formatLabel": "Format", - "netscape": "Netscape TXT", - "json": "JSON", - "success": "Cookies exportés avec succès", - "error": "Échec de l'exportation des cookies" - } - }, "profileInfo": { "title": "Détails du Profil", "tabs": { diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index 430d95b..ceacea3 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -524,7 +524,29 @@ "selectedCount_plural": "{{count}} 個のCookieを選択" }, "success": "Cookieが正常にコピーされました", - "error": "Cookieのコピーに失敗しました" + "error": "Cookieのコピーに失敗しました", + "management": { + "title": "Cookie管理", + "menuItem": "Cookie管理" + }, + "import": { + "title": "Cookieのインポート", + "description": "NetscapeまたはJSON形式のファイルからCookieをインポートします。", + "selectFile": "ファイルを選択", + "preview": "{{count}}件のCookieが見つかりました", + "success": "{{imported}}件のCookieをインポートしました({{replaced}}件を置換)", + "error": "Cookieのインポートに失敗しました", + "proFeature": "Cookieのインポートはプロ機能です" + }, + "export": { + "title": "Cookieのエクスポート", + "description": "このプロファイルからCookieをエクスポートします。", + "formatLabel": "形式", + "netscape": "Netscape TXT", + "json": "JSON", + "success": "Cookieのエクスポートに成功しました", + "error": "Cookieのエクスポートに失敗しました" + } }, "toasts": { "success": { @@ -724,30 +746,6 @@ "cannotLaunch": "このプロファイルは{{os}}で作成されたもので、このシステムではサポートされていません", "cannotModify": "他のOSのプロファイルの同期設定は変更できません" }, - "cookies": { - "management": { - "title": "Cookie管理", - "menuItem": "Cookie管理" - }, - "import": { - "title": "Cookieのインポート", - "description": "NetscapeまたはJSON形式のファイルからCookieをインポートします。", - "selectFile": "ファイルを選択", - "preview": "{{count}}件のCookieが見つかりました", - "success": "{{imported}}件のCookieをインポートしました({{replaced}}件を置換)", - "error": "Cookieのインポートに失敗しました", - "proFeature": "Cookieのインポートはプロ機能です" - }, - "export": { - "title": "Cookieのエクスポート", - "description": "このプロファイルからCookieをエクスポートします。", - "formatLabel": "形式", - "netscape": "Netscape TXT", - "json": "JSON", - "success": "Cookieのエクスポートに成功しました", - "error": "Cookieのエクスポートに失敗しました" - } - }, "profileInfo": { "title": "プロフィール詳細", "tabs": { diff --git a/src/i18n/locales/pt.json b/src/i18n/locales/pt.json index 6ae0ff6..5ee1630 100644 --- a/src/i18n/locales/pt.json +++ b/src/i18n/locales/pt.json @@ -524,7 +524,29 @@ "selectedCount_plural": "{{count}} cookies selecionados" }, "success": "Cookies copiados com sucesso", - "error": "Falha ao copiar cookies" + "error": "Falha ao copiar cookies", + "management": { + "title": "Gerenciamento de Cookies", + "menuItem": "Gerenciamento de Cookies" + }, + "import": { + "title": "Importar Cookies", + "description": "Importar cookies de um arquivo no formato Netscape ou JSON.", + "selectFile": "Escolher Arquivo", + "preview": "{{count}} cookies encontrados", + "success": "{{imported}} cookies importados com sucesso ({{replaced}} substituídos)", + "error": "Falha ao importar cookies", + "proFeature": "A importação de cookies é um recurso Pro" + }, + "export": { + "title": "Exportar Cookies", + "description": "Exportar cookies deste perfil.", + "formatLabel": "Formato", + "netscape": "Netscape TXT", + "json": "JSON", + "success": "Cookies exportados com sucesso", + "error": "Falha ao exportar cookies" + } }, "toasts": { "success": { @@ -724,30 +746,6 @@ "cannotLaunch": "Este perfil foi criado em {{os}} e não é compatível com este sistema", "cannotModify": "Não é possível modificar as configurações de sincronização de um perfil de outro sistema operacional" }, - "cookies": { - "management": { - "title": "Gerenciamento de Cookies", - "menuItem": "Gerenciamento de Cookies" - }, - "import": { - "title": "Importar Cookies", - "description": "Importar cookies de um arquivo no formato Netscape ou JSON.", - "selectFile": "Escolher Arquivo", - "preview": "{{count}} cookies encontrados", - "success": "{{imported}} cookies importados com sucesso ({{replaced}} substituídos)", - "error": "Falha ao importar cookies", - "proFeature": "A importação de cookies é um recurso Pro" - }, - "export": { - "title": "Exportar Cookies", - "description": "Exportar cookies deste perfil.", - "formatLabel": "Formato", - "netscape": "Netscape TXT", - "json": "JSON", - "success": "Cookies exportados com sucesso", - "error": "Falha ao exportar cookies" - } - }, "profileInfo": { "title": "Detalhes do Perfil", "tabs": { diff --git a/src/i18n/locales/ru.json b/src/i18n/locales/ru.json index 2f829a1..ca704e7 100644 --- a/src/i18n/locales/ru.json +++ b/src/i18n/locales/ru.json @@ -524,7 +524,29 @@ "selectedCount_plural": "Выбрано {{count}} cookie" }, "success": "Cookie успешно скопированы", - "error": "Ошибка копирования cookie" + "error": "Ошибка копирования cookie", + "management": { + "title": "Управление Cookies", + "menuItem": "Управление Cookies" + }, + "import": { + "title": "Импорт Cookies", + "description": "Импорт cookies из файла в формате Netscape или JSON.", + "selectFile": "Выбрать файл", + "preview": "Найдено {{count}} cookies", + "success": "Успешно импортировано {{imported}} cookies ({{replaced}} заменено)", + "error": "Ошибка импорта cookies", + "proFeature": "Импорт cookies — функция Pro" + }, + "export": { + "title": "Экспорт Cookies", + "description": "Экспорт cookies из этого профиля.", + "formatLabel": "Формат", + "netscape": "Netscape TXT", + "json": "JSON", + "success": "Cookies успешно экспортированы", + "error": "Ошибка экспорта cookies" + } }, "toasts": { "success": { @@ -724,30 +746,6 @@ "cannotLaunch": "Этот профиль был создан на {{os}} и не поддерживается в этой системе", "cannotModify": "Невозможно изменить настройки синхронизации профиля другой ОС" }, - "cookies": { - "management": { - "title": "Управление Cookies", - "menuItem": "Управление Cookies" - }, - "import": { - "title": "Импорт Cookies", - "description": "Импорт cookies из файла в формате Netscape или JSON.", - "selectFile": "Выбрать файл", - "preview": "Найдено {{count}} cookies", - "success": "Успешно импортировано {{imported}} cookies ({{replaced}} заменено)", - "error": "Ошибка импорта cookies", - "proFeature": "Импорт cookies — функция Pro" - }, - "export": { - "title": "Экспорт Cookies", - "description": "Экспорт cookies из этого профиля.", - "formatLabel": "Формат", - "netscape": "Netscape TXT", - "json": "JSON", - "success": "Cookies успешно экспортированы", - "error": "Ошибка экспорта cookies" - } - }, "profileInfo": { "title": "Детали профиля", "tabs": { diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index 5bb4778..ca6ae2e 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -524,7 +524,29 @@ "selectedCount_plural": "已选择 {{count}} 个 cookies" }, "success": "Cookies 复制成功", - "error": "复制 cookies 失败" + "error": "复制 cookies 失败", + "management": { + "title": "Cookie 管理", + "menuItem": "Cookie 管理" + }, + "import": { + "title": "导入 Cookies", + "description": "从 Netscape 或 JSON 格式文件导入 Cookies。", + "selectFile": "选择文件", + "preview": "找到 {{count}} 个 Cookies", + "success": "成功导入 {{imported}} 个 Cookies(替换了 {{replaced}} 个)", + "error": "导入 Cookies 失败", + "proFeature": "导入 Cookies 是 Pro 功能" + }, + "export": { + "title": "导出 Cookies", + "description": "从此配置文件导出 Cookies。", + "formatLabel": "格式", + "netscape": "Netscape TXT", + "json": "JSON", + "success": "Cookies 导出成功", + "error": "导出 Cookies 失败" + } }, "toasts": { "success": { @@ -724,30 +746,6 @@ "cannotLaunch": "此配置文件在 {{os}} 上创建,不受此系统支持", "cannotModify": "无法修改跨操作系统配置文件的同步设置" }, - "cookies": { - "management": { - "title": "Cookie 管理", - "menuItem": "Cookie 管理" - }, - "import": { - "title": "导入 Cookies", - "description": "从 Netscape 或 JSON 格式文件导入 Cookies。", - "selectFile": "选择文件", - "preview": "找到 {{count}} 个 Cookies", - "success": "成功导入 {{imported}} 个 Cookies(替换了 {{replaced}} 个)", - "error": "导入 Cookies 失败", - "proFeature": "导入 Cookies 是 Pro 功能" - }, - "export": { - "title": "导出 Cookies", - "description": "从此配置文件导出 Cookies。", - "formatLabel": "格式", - "netscape": "Netscape TXT", - "json": "JSON", - "success": "Cookies 导出成功", - "error": "导出 Cookies 失败" - } - }, "profileInfo": { "title": "配置文件详情", "tabs": { diff --git a/src/types.ts b/src/types.ts index 3ee33cd..258c9af 100644 --- a/src/types.ts +++ b/src/types.ts @@ -341,13 +341,13 @@ export interface CamoufoxFingerprintConfig { "canvas:aaCapOffset"?: boolean; // Voices - voices?: Array<{ + voices?: { isLocalService?: boolean; isDefault?: boolean; voiceURI?: string; name?: string; lang?: string; - }>; + }[]; "voices:blockIfNotDefined"?: boolean; "voices:fakeCompletion"?: boolean; "voices:fakeCompletion:charsPerSecond"?: number;