Skip to content

Commit fe14312

Browse files
committed
LAN-File-Shuttle-Pro
1 parent 565f60e commit fe14312

18 files changed

+2341
-0
lines changed

LAN-File-Shuttle-Pro.py

Lines changed: 788 additions & 0 deletions
Large diffs are not rendered by default.

app_Idea.py

Lines changed: 684 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
pnpm-debug.log*
8+
lerna-debug.log*
9+
10+
node_modules
11+
dist
12+
dist-ssr
13+
*.local
14+
15+
# Editor directories and files
16+
.vscode/*
17+
!.vscode/extensions.json
18+
.idea
19+
.DS_Store
20+
*.suo
21+
*.ntvs*
22+
*.njsproj
23+
*.sln
24+
*.sw?

lan-file-shuttle_Web-App/App.tsx

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
2+
import React, { useState } from 'react';
3+
import { SenderView } from './components/SenderView';
4+
import { ReceiverView } from './components/ReceiverView';
5+
import { HomeIcon, UploadIcon, DownloadIcon } from './constants';
6+
import type { AppView } from './types';
7+
8+
const App: React.FC = () => {
9+
const [view, setView] = useState<AppView>('home');
10+
11+
const renderView = () => {
12+
switch (view) {
13+
case 'sender':
14+
return <SenderView />;
15+
case 'receiver':
16+
return <ReceiverView />;
17+
default:
18+
return (
19+
<div className="text-center">
20+
<h2 className="text-3xl font-bold text-gray-200 mb-2">Welcome to LAN File Shuttle</h2>
21+
<p className="text-gray-400 mb-8">Choose your role to begin a simulated transfer.</p>
22+
<div className="flex flex-col md:flex-row justify-center gap-6">
23+
<button
24+
onClick={() => setView('sender')}
25+
className="flex items-center justify-center gap-3 bg-blue-600 hover:bg-blue-700 text-white font-bold py-4 px-6 rounded-lg shadow-lg transition-transform transform hover:scale-105"
26+
>
27+
<UploadIcon />
28+
Send Files
29+
</button>
30+
<button
31+
onClick={() => setView('receiver')}
32+
className="flex items-center justify-center gap-3 bg-indigo-600 hover:bg-indigo-700 text-white font-bold py-4 px-6 rounded-lg shadow-lg transition-transform transform hover:scale-105"
33+
>
34+
<DownloadIcon />
35+
Receive Files
36+
</button>
37+
</div>
38+
</div>
39+
);
40+
}
41+
};
42+
43+
return (
44+
<div className="bg-gray-900 min-h-screen text-white p-4 sm:p-6 md:p-8 flex flex-col items-center">
45+
<div className="w-full max-w-4xl">
46+
<header className="flex items-center justify-between mb-8">
47+
<div className="flex items-center gap-3">
48+
<svg className="w-10 h-10 text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path></svg>
49+
<h1 className="text-2xl sm:text-3xl font-bold text-gray-100">LAN File Shuttle</h1>
50+
</div>
51+
{view !== 'home' && (
52+
<button
53+
onClick={() => setView('home')}
54+
className="flex items-center gap-2 bg-gray-700 hover:bg-gray-600 text-white font-semibold py-2 px-4 rounded-lg transition-colors"
55+
>
56+
<HomeIcon />
57+
Home
58+
</button>
59+
)}
60+
</header>
61+
<main className="bg-gray-800 rounded-xl shadow-2xl p-6 sm:p-8">
62+
{renderView()}
63+
</main>
64+
<footer className="text-center mt-8 text-gray-500 text-sm">
65+
<p>&copy; 2024 LAN File Shuttle. All rights reserved. This is a UI simulation.</p>
66+
</footer>
67+
</div>
68+
</div>
69+
);
70+
};
71+
72+
export default App;

lan-file-shuttle_Web-App/README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<div align="center">
2+
<img width="1200" height="475" alt="GHBanner" src="https://github.com/user-attachments/assets/0aa67016-6eaf-458a-adb2-6e31a0763ed6" />
3+
</div>
4+
5+
# Run and deploy your AI Studio app
6+
7+
This contains everything you need to run your app locally.
8+
9+
View your app in AI Studio: https://ai.studio/apps/drive/1wftgXH45hk2al3DyitS0EawK4TXeOhWj
10+
11+
## Run Locally
12+
13+
**Prerequisites:** Node.js
14+
15+
16+
1. Install dependencies:
17+
`npm install`
18+
2. Set the `GEMINI_API_KEY` in [.env.local](.env.local) to your Gemini API key
19+
3. Run the app:
20+
`npm run dev`
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
2+
import React, { useRef, useEffect } from 'react';
3+
import type { LogMessage } from '../types';
4+
import { InfoIcon, SuccessIcon, ErrorIcon } from '../constants';
5+
6+
interface LogViewProps {
7+
logs: LogMessage[];
8+
}
9+
10+
const logIcons = {
11+
info: <InfoIcon />,
12+
success: <SuccessIcon />,
13+
error: <ErrorIcon />
14+
};
15+
16+
const logColors = {
17+
info: 'text-gray-300',
18+
success: 'text-green-300',
19+
error: 'text-red-300'
20+
};
21+
22+
export const LogView: React.FC<LogViewProps> = ({ logs }) => {
23+
const logContainerRef = useRef<HTMLDivElement>(null);
24+
25+
useEffect(() => {
26+
if (logContainerRef.current) {
27+
logContainerRef.current.scrollTop = logContainerRef.current.scrollHeight;
28+
}
29+
}, [logs]);
30+
31+
if (logs.length === 0) return null;
32+
33+
return (
34+
<div className="mt-8">
35+
<h3 className="font-semibold text-lg mb-2 text-gray-400">Status Log</h3>
36+
<div
37+
ref={logContainerRef}
38+
className="bg-gray-900 rounded-lg p-4 h-48 overflow-y-auto font-mono text-sm space-y-2 border border-gray-700"
39+
>
40+
{logs.map((log) => (
41+
<div key={log.id} className={`flex items-start gap-3 ${logColors[log.type]}`}>
42+
<div className="mt-0.5">{logIcons[log.type]}</div>
43+
<div className="flex-grow">
44+
<span className="text-gray-500 mr-2">{log.timestamp}</span>
45+
<span>{log.message}</span>
46+
</div>
47+
</div>
48+
))}
49+
</div>
50+
</div>
51+
);
52+
};
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
2+
import React, { useState, useCallback, useRef, useEffect } from 'react';
3+
import type { LogMessage, TransferProgress } from '../types';
4+
import { TransferStatus } from '../types';
5+
import { LogView } from './LogView';
6+
import { TransferDisplay } from './TransferDisplay';
7+
import { SIMULATION_CHUNK_SIZE, SIMULATION_INTERVAL } from '../constants';
8+
9+
// Mock file data for receiver simulation
10+
const MOCK_FILES: File[] = [
11+
new File(["data"], "project-alpha.zip", { type: "application/zip" }),
12+
new File(["data"], "meeting-notes.docx", { type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document" }),
13+
new File(["data"], "design-mockup-v3.png", { type: "image/png" }),
14+
];
15+
// Manually setting sizes as File constructor doesn't allow it directly
16+
Object.defineProperty(MOCK_FILES[0], 'size', { value: 157286400 }); // 150 MB
17+
Object.defineProperty(MOCK_FILES[1], 'size', { value: 2097152 }); // 2 MB
18+
Object.defineProperty(MOCK_FILES[2], 'size', { value: 5242880 }); // 5 MB
19+
20+
export const ReceiverView: React.FC = () => {
21+
const [transferCode, setTransferCode] = useState('');
22+
const [status, setStatus] = useState<TransferStatus>(TransferStatus.IDLE);
23+
const [logs, setLogs] = useState<LogMessage[]>([]);
24+
const [progress, setProgress] = useState<TransferProgress>({
25+
percentage: 0, speed: 0, sentBytes: 0, totalBytes: 0, eta: 0
26+
});
27+
28+
const transferIntervalRef = useRef<number | null>(null);
29+
30+
const addLog = useCallback((message: string, type: 'info' | 'success' | 'error' = 'info') => {
31+
setLogs(prev => [
32+
...prev,
33+
{ id: Date.now(), type, message, timestamp: new Date().toLocaleTimeString() }
34+
]);
35+
}, []);
36+
37+
useEffect(() => {
38+
return () => {
39+
if (transferIntervalRef.current) {
40+
clearInterval(transferIntervalRef.current);
41+
}
42+
};
43+
}, []);
44+
45+
const handleConnect = () => {
46+
if (!/^[A-Z0-9]{6}$/.test(transferCode)) {
47+
addLog('Invalid transfer code. Must be 6 uppercase letters/numbers.', 'error');
48+
return;
49+
}
50+
addLog(`Attempting to connect with code: ${transferCode}...`);
51+
setStatus(TransferStatus.CONNECTING);
52+
53+
setTimeout(() => {
54+
addLog('Connection established. Preparing to receive files.');
55+
startReceiving();
56+
}, 1500);
57+
};
58+
59+
const startReceiving = () => {
60+
setStatus(TransferStatus.TRANSFERRING);
61+
const totalBytes = MOCK_FILES.reduce((acc, file) => acc + file.size, 0);
62+
setProgress({ percentage: 0, speed: 0, sentBytes: 0, totalBytes, eta: Infinity });
63+
addLog(`Receiving ${MOCK_FILES.length} files (${(totalBytes / (1024 * 1024)).toFixed(2)} MB)...`);
64+
65+
let receivedBytes = 0;
66+
const startTime = Date.now();
67+
68+
transferIntervalRef.current = window.setInterval(() => {
69+
const elapsedSeconds = (Date.now() - startTime) / 1000;
70+
receivedBytes += SIMULATION_CHUNK_SIZE * (0.8 + Math.random() * 0.4);
71+
receivedBytes = Math.min(receivedBytes, totalBytes);
72+
73+
const percentage = (receivedBytes / totalBytes) * 100;
74+
const speed = elapsedSeconds > 0 ? (receivedBytes / elapsedSeconds) / (1024 * 1024) : 0;
75+
const remainingBytes = totalBytes - receivedBytes;
76+
const eta = speed > 0 ? remainingBytes / (speed * 1024 * 1024) : Infinity;
77+
78+
setProgress({ percentage, speed, sentBytes: receivedBytes, totalBytes, eta });
79+
80+
if (receivedBytes >= totalBytes) {
81+
if (transferIntervalRef.current) clearInterval(transferIntervalRef.current);
82+
setStatus(TransferStatus.COMPLETE);
83+
addLog('File reception complete!', 'success');
84+
}
85+
}, SIMULATION_INTERVAL);
86+
};
87+
88+
const handleStopTransfer = () => {
89+
if (transferIntervalRef.current) {
90+
clearInterval(transferIntervalRef.current);
91+
transferIntervalRef.current = null;
92+
}
93+
setStatus(TransferStatus.STOPPED);
94+
addLog('Reception stopped by user.', 'error');
95+
};
96+
97+
const resetState = () => {
98+
setTransferCode('');
99+
setStatus(TransferStatus.IDLE);
100+
setLogs([]);
101+
setProgress({ percentage: 0, speed: 0, sentBytes: 0, totalBytes: 0, eta: 0 });
102+
};
103+
104+
const isTransmitting = status === TransferStatus.TRANSFERRING || status === TransferStatus.CONNECTING;
105+
const isFinished = status === TransferStatus.COMPLETE || status === TransferStatus.FAILED || status === TransferStatus.STOPPED;
106+
107+
return (
108+
<div>
109+
<h2 className="text-2xl font-bold text-gray-200 mb-6">Receive Files</h2>
110+
111+
{!isTransmitting && !isFinished && (
112+
<div className="flex flex-col sm:flex-row items-center gap-4">
113+
<input
114+
type="text"
115+
value={transferCode}
116+
onChange={(e) => setTransferCode(e.target.value.toUpperCase())}
117+
placeholder="Enter 6-digit code"
118+
maxLength={6}
119+
className="w-full sm:w-auto flex-grow text-center font-mono text-2xl tracking-widest bg-gray-900 border-2 border-gray-600 rounded-lg p-3 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"
120+
/>
121+
<button
122+
onClick={handleConnect}
123+
disabled={transferCode.length !== 6}
124+
className="w-full sm:w-auto bg-indigo-600 hover:bg-indigo-700 text-white font-bold py-3 px-8 rounded-lg disabled:bg-gray-500 disabled:cursor-not-allowed transition-colors text-lg"
125+
>
126+
Connect
127+
</button>
128+
</div>
129+
)}
130+
131+
{(isTransmitting || isFinished) && (
132+
<TransferDisplay
133+
status={status}
134+
progress={progress}
135+
transferCode={transferCode}
136+
files={MOCK_FILES}
137+
isSender={false}
138+
/>
139+
)}
140+
141+
{isTransmitting && !isFinished && status !== TransferStatus.CONNECTING && (
142+
<div className="mt-8 flex justify-center">
143+
<button
144+
onClick={handleStopTransfer}
145+
className="bg-red-600 hover:bg-red-700 text-white font-bold py-3 px-8 rounded-lg transition-colors text-lg"
146+
>
147+
Stop Transfer
148+
</button>
149+
</div>
150+
)}
151+
152+
{isFinished && (
153+
<div className="mt-8 flex justify-center">
154+
<button
155+
onClick={resetState}
156+
className="bg-indigo-600 hover:bg-indigo-700 text-white font-bold py-3 px-8 rounded-lg transition-colors text-lg"
157+
>
158+
Receive New Files
159+
</button>
160+
</div>
161+
)}
162+
163+
<LogView logs={logs} />
164+
</div>
165+
);
166+
};

0 commit comments

Comments
 (0)