Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
273 changes: 143 additions & 130 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
"driver.js": "^1.4.0",
"embla-carousel-react": "^8.6.0",
"i18next": "^25.4.2",
"input-otp": "^1.4.2",
Expand Down
25 changes: 24 additions & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import "./driver.css";
import {
Tabs,
TabsContent,
Expand All @@ -13,6 +14,7 @@ import {
Send,
History,
Zap,
HelpCircle,
} from "lucide-react";
import { DeviceDetection } from "./components/DeviceDetection";
import { FileTransfer } from "./components/FileTransfer";
Expand All @@ -29,6 +31,7 @@ import { AutoAcceptNotification } from './components/AutoAcceptNotification';
import { TransferNotification } from './components/TransferNotification';
import { invoke } from "@tauri-apps/api/core";
import { listen } from "@tauri-apps/api/event";
import { useTour } from "./hooks/useTour";

interface UploadFile {
name: string;
Expand All @@ -52,9 +55,17 @@ type BackendTransferRecord = {

function AppContent() {
const { t } = useTranslation();

const [selectedDevices, setSelectedDevices] = useState<any[]>([]);
const [selectedFiles, setSelectedFiles] = useState<UploadFile[]>([]);
const [activeTab, setActiveTab] = useState("transfer");

const { startTour, checkAndStartTour } = useTour(setActiveTab);

useEffect(() => {
checkAndStartTour();
}, []);
Copy link

Copilot AI Jan 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The checkAndStartTour function is called in useEffect without being included in the dependency array. This violates React's exhaustive-deps rule and could lead to stale closures. Either add checkAndStartTour to the dependency array or wrap it with useCallback to ensure it has a stable reference.

Suggested change
}, []);
}, [checkAndStartTour]);

Copilot uses AI. Check for mistakes.

const [avgSpeedToday, setAvgSpeedToday] = useState<number>(0);
const [route, setRoute] = useState<string>(window.location.hash || "");

Expand Down Expand Up @@ -146,7 +157,7 @@ function AppContent() {
</div>

{/* Header */}
<header className="border-b backdrop-blur-md bg-background/85 dark:bg-background/70 sticky top-0 z-50 relative">
<header id="app-header" className="border-b backdrop-blur-md bg-background/85 dark:bg-background/70 sticky top-0 z-50 relative">
<div className="max-w-6xl mx-auto px-6 py-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
Expand Down Expand Up @@ -174,6 +185,14 @@ function AppContent() {
<div className="w-2 h-2 bg-green-500 rounded-full animate-pulse" />
{t('common.online')}
</Badge>
<button
id="start-tour-btn"
onClick={startTour}
className="p-2 rounded-full hover:bg-secondary/80 transition-colors"
title={t('tour.start', 'Start Tour')}
Copy link

Copilot AI Jan 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tour button is missing an aria-label attribute. While it has a title attribute, an aria-label would improve accessibility for screen reader users. Add aria-label to make the button's purpose clear to assistive technologies.

Suggested change
title={t('tour.start', 'Start Tour')}
title={t('tour.start', 'Start Tour')}
aria-label={t('tour.start', 'Start Tour')}

Copilot uses AI. Check for mistakes.
>
<HelpCircle className="w-5 h-5 text-muted-foreground" />
</button>
</div>
</div>
</div>
Expand Down Expand Up @@ -217,6 +236,7 @@ function AppContent() {
<div className="relative">
<TabsList className="flex w-full lg:w-[950px] h-16 bg-background/80 dark:bg-background/60 backdrop-blur-md border border-border/50 p-1 gap-1">
<TabsTrigger
id="tab-transfer"
value="transfer"
className="relative overflow-hidden group h-full px-6 py-3 data-[state=active]:bg-transparent flex-1 transition-all duration-300"
>
Expand All @@ -238,6 +258,7 @@ function AppContent() {
</div>
</TabsTrigger>
<TabsTrigger
id="tab-devices"
value="devices"
className="relative overflow-hidden group h-full px-6 py-3 data-[state=active]:bg-transparent flex-1 transition-all duration-300"
>
Expand All @@ -259,6 +280,7 @@ function AppContent() {
</div>
</TabsTrigger>
<TabsTrigger
id="tab-history"
value="history"
className="relative overflow-hidden group h-full px-6 py-3 data-[state=active]:bg-transparent flex-1 transition-all duration-300"
>
Expand All @@ -280,6 +302,7 @@ function AppContent() {
</div>
</TabsTrigger>
<TabsTrigger
id="tab-settings"
value="settings"
className="relative overflow-hidden group h-full px-6 py-3 data-[state=active]:bg-transparent flex-1 transition-all duration-300"
>
Expand Down
2 changes: 1 addition & 1 deletion src/components/DeviceDetection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export function DeviceDetection({ onDeviceSelect, selectedDevices }: DeviceDetec
};

return (
<div className="space-y-6">
<div id="device-list-container" className="space-y-6">
Copy link

Copilot AI Jan 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ID 'device-list-container' is assigned to two different components - both DeviceDetection.tsx and DeviceList.tsx use the same ID. This creates a duplicate ID in the DOM when both components are rendered, which violates HTML standards and can cause issues with the tour functionality. Each element should have a unique ID. Consider using 'device-detection-container' for DeviceDetection.tsx or 'devices-tab-container' to differentiate it.

Suggested change
<div id="device-list-container" className="space-y-6">
<div id="device-detection-container" className="space-y-6">

Copilot uses AI. Check for mistakes.
{/* Header */}
<motion.div
initial={{ opacity: 0, y: 20 }}
Expand Down
2 changes: 1 addition & 1 deletion src/components/DeviceList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ export function DeviceList({ selectedDevices, onSelectionChange }: DeviceListPro
};

return (
<Card className="backdrop-blur-md bg-gray-900/40 border border-gray-700/50 shadow-2xl p-6 w-full">
<Card id="device-list-container" className="backdrop-blur-md bg-gray-900/40 border border-gray-700/50 shadow-2xl p-6 w-full">
<div className="flex items-center justify-between mb-6">
<div className="flex items-center gap-3">
<div className="p-2 rounded-full bg-gradient-to-r from-gray-700/40 to-slate-600/40 backdrop-blur-sm">
Expand Down
2 changes: 2 additions & 0 deletions src/components/DynamicSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,7 @@ export function DynamicSidebar({ selectedDevices, networkSpeed, context }: Dynam
{stats.map((_, index) => (
<button
key={index}
aria-label={`Show stat ${index + 1}`}
onClick={() => setCurrentStatsIndex(index)}
className={`w-2 h-2 rounded-full transition-colors ${
index === currentStatsIndex ? 'bg-primary' : 'bg-muted'
Expand Down Expand Up @@ -495,6 +496,7 @@ export function DynamicSidebar({ selectedDevices, networkSpeed, context }: Dynam
{tips.map((_, index) => (
<button
key={index}
aria-label={`Show tip ${index + 1}`}
onClick={() => setCurrentTipIndex(index)}
className={`w-2 h-2 rounded-full transition-colors ${
index === currentTipIndex ? 'bg-primary' : 'bg-muted'
Expand Down
1 change: 1 addition & 0 deletions src/components/FileTransfer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,7 @@ export function FileTransfer({

{/* File drop area */}
<div
id="file-drop-zone"
onDrop={handleDrop}
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
Expand Down
4 changes: 2 additions & 2 deletions src/components/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export function Settings() {
</div>

{/* Appearance Settings */}
<GlassCard className="p-6">
<GlassCard className="p-6" id="appearance-settings">
<div className="space-y-4">
<div className="flex items-center gap-2">
<Palette className="w-4 h-4" />
Expand Down Expand Up @@ -155,7 +155,7 @@ export function Settings() {
</GlassCard>

{/* Language Settings */}
<GlassCard className="p-6">
<GlassCard className="p-6" id="language-settings">
<div className="space-y-4">
<div className="flex items-center gap-2">
<Globe className="w-4 h-4" />
Expand Down
2 changes: 1 addition & 1 deletion src/components/TransferHistory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ export function TransferHistory() {
};

return (
<div className="space-y-6">
<div className="space-y-6" id="history-list">
{/* Header with Stats */}
<motion.div
initial={{ opacity: 0, y: 20 }}
Expand Down
73 changes: 73 additions & 0 deletions src/driver.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@

/* Driver.js Custom Theme */
.driverjs-theme {
background-color: var(--card) !important;
color: var(--card-foreground) !important;
border-radius: 1rem !important;
box-shadow: 0 10px 30px -10px rgba(0,0,0,0.2) !important;
border: 1px solid var(--border) !important;
font-family: inherit !important;
max-width: 350px !important;
}

.driverjs-theme .driver-popover-title {
font-size: 1.1rem !important;
font-weight: 600 !important;
margin-bottom: 0.5rem !important;
color: var(--primary) !important;
font-family: inherit !important;
}

.driverjs-theme .driver-popover-description {
font-size: 0.95rem !important;
line-height: 1.5 !important;
color: var(--muted-foreground) !important;
margin-bottom: 1rem !important;
font-family: inherit !important;
}

.driverjs-theme .driver-popover-footer {
display: flex !important;
gap: 0.5rem !important;
justify-content: flex-end !important;
margin-top: 0.5rem !important;
}

.driverjs-theme .driver-popover-btn {
border-radius: 0.5rem !important;
padding: 0.4rem 0.8rem !important;
font-size: 0.85rem !important;
font-weight: 500 !important;
transition: all 0.2s ease !important;
text-shadow: none !important;
border: none !important;
font-family: inherit !important;
}

.driverjs-theme .driver-popover-prev-btn {
background-color: var(--secondary) !important;
color: var(--secondary-foreground) !important;
border: 1px solid var(--border) !important;
}

.driverjs-theme .driver-popover-prev-btn:hover {
background-color: hsl(var(--secondary) / 0.8) !important;
}

.driverjs-theme .driver-popover-next-btn {
background-color: hsl(var(--primary)) !important;
color: hsl(var(--primary-foreground)) !important;
}

.driverjs-theme .driver-popover-next-btn:hover {
background-color: hsl(var(--primary) / 0.8) !important;
}

.driverjs-theme .driver-popover-close-btn {
color: hsl(var(--muted-foreground)) !important;
}

.driverjs-theme .driver-popover-progress-text {
color: hsl(var(--muted-foreground)) !important;
font-size: 0.75rem !important;
}
Loading
Loading