fix(remote): 上传下载现在使用文件面板当前选择的路径
This commit is contained in:
@@ -1,138 +0,0 @@
|
||||
{
|
||||
"name": "xcdesktop",
|
||||
"version": "0.0.0",
|
||||
"description": "一站式 AI 工作台 - 集成笔记管理、时间追踪、任务管理、AI 辅助等多种功能",
|
||||
"keywords": [
|
||||
"markdown",
|
||||
"note",
|
||||
"笔记",
|
||||
"electron",
|
||||
"react",
|
||||
"typescript",
|
||||
"时间追踪",
|
||||
"任务管理"
|
||||
],
|
||||
"author": "Your Name",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/your-username/xcdesktop.git"
|
||||
},
|
||||
"private": true,
|
||||
"main": "dist-electron/main.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"client:dev": "vite",
|
||||
"build": "tsc --noEmit -p tsconfig.json && tsc --noEmit -p tsconfig.api.json && vite build",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview",
|
||||
"check": "tsc --noEmit -p tsconfig.json && tsc --noEmit -p tsconfig.api.json",
|
||||
"test": "vitest",
|
||||
"test:ui": "vitest --ui",
|
||||
"test:coverage": "vitest --coverage",
|
||||
"test:run": "vitest run",
|
||||
"server:dev": "nodemon",
|
||||
"dev": "concurrently \"npm run client:dev\" \"npm run server:dev\"",
|
||||
"watch:electron": "tsup --config tsup.electron.ts --watch",
|
||||
"electron:dev": "concurrently -k \"npm run server:dev\" \"npm run client:dev\" \"wait-on tcp:3001 tcp:5173 && npm run watch:electron\"",
|
||||
"build:electron": "tsup --config tsup.electron.ts",
|
||||
"build:api": "tsup electron/server.ts --format esm --out-dir dist-api --clean --no-splitting --target esnext",
|
||||
"electron:build": "npm run build && npm run build:electron && npm run build:api && electron-builder"
|
||||
},
|
||||
"dependencies": {
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@dnd-kit/sortable": "^10.0.0",
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
"@milkdown/core": "^7.18.0",
|
||||
"@milkdown/plugin-block": "^7.18.0",
|
||||
"@milkdown/plugin-history": "^7.18.0",
|
||||
"@milkdown/plugin-listener": "^7.18.0",
|
||||
"@milkdown/plugin-math": "^7.5.9",
|
||||
"@milkdown/plugin-prism": "^7.18.0",
|
||||
"@milkdown/preset-commonmark": "^7.18.0",
|
||||
"@milkdown/preset-gfm": "^7.18.0",
|
||||
"@milkdown/react": "^7.18.0",
|
||||
"@types/jszip": "^3.4.0",
|
||||
"@types/multer": "^2.0.0",
|
||||
"@types/prismjs": "^1.26.5",
|
||||
"axios": "^1.13.5",
|
||||
"chokidar": "^5.0.0",
|
||||
"clsx": "^2.1.1",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^17.2.1",
|
||||
"electron-log": "^5.4.3",
|
||||
"express": "^4.21.2",
|
||||
"github-slugger": "^2.0.0",
|
||||
"jszip": "^3.10.1",
|
||||
"lucide-react": "^0.511.0",
|
||||
"multer": "^2.0.2",
|
||||
"prismjs": "^1.30.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-router-dom": "^7.3.0",
|
||||
"recharts": "^3.7.0",
|
||||
"remark-breaks": "^4.0.0",
|
||||
"zod": "^4.3.6",
|
||||
"zustand": "^5.0.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.25.0",
|
||||
"@tailwindcss/typography": "^0.5.19",
|
||||
"@testing-library/jest-dom": "^6.9.1",
|
||||
"@testing-library/react": "^16.3.2",
|
||||
"@types/cors": "^2.8.19",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/node": "^22.19.13",
|
||||
"@types/react": "^18.3.12",
|
||||
"@types/react-dom": "^18.3.1",
|
||||
"@vercel/node": "^5.3.6",
|
||||
"@vitejs/plugin-react": "^4.4.1",
|
||||
"@vitest/ui": "^4.0.18",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"babel-plugin-react-dev-locator": "^1.0.0",
|
||||
"concurrently": "^9.2.0",
|
||||
"electron": "^40.2.1",
|
||||
"electron-builder": "^26.7.0",
|
||||
"eslint": "^9.25.0",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.19",
|
||||
"globals": "^16.0.0",
|
||||
"happy-dom": "^20.7.0",
|
||||
"jsdom": "^28.1.0",
|
||||
"nodemon": "^3.1.10",
|
||||
"postcss": "^8.5.3",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"tsup": "^8.5.1",
|
||||
"tsx": "^4.20.3",
|
||||
"typescript": "~5.8.3",
|
||||
"typescript-eslint": "^8.30.1",
|
||||
"vite": "^6.3.5",
|
||||
"vite-tsconfig-paths": "^5.1.4",
|
||||
"vitest": "^4.0.18",
|
||||
"wait-on": "^9.0.3"
|
||||
},
|
||||
"build": {
|
||||
"appId": "com.xcdesktop.app",
|
||||
"productName": "XCDesktop",
|
||||
"directories": {
|
||||
"output": "release"
|
||||
},
|
||||
"files": [
|
||||
"dist/**/*",
|
||||
"dist-electron/**/*",
|
||||
"dist-api/**/*",
|
||||
"shared/**/*",
|
||||
"tools/**/*",
|
||||
"package.json"
|
||||
],
|
||||
"asarUnpack": [
|
||||
"tools/**/*"
|
||||
],
|
||||
"win": {
|
||||
"target": "nsis"
|
||||
},
|
||||
"nsis": {
|
||||
"oneClick": false,
|
||||
"allowToChangeInstallationDirectory": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ export interface RemoteDevice {
|
||||
serverHost: string
|
||||
desktopPort: number
|
||||
gitPort: number
|
||||
openCodePort: number
|
||||
password?: string
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ const createEmptyDevice = (): RemoteDevice => ({
|
||||
serverHost: '',
|
||||
desktopPort: 3000,
|
||||
gitPort: 3001,
|
||||
openCodePort: 3002,
|
||||
})
|
||||
|
||||
export const RemotePage: React.FC = () => {
|
||||
@@ -81,6 +82,13 @@ export const RemotePage: React.FC = () => {
|
||||
try {
|
||||
const savedConfig = await getRemoteConfig()
|
||||
const configWithDevices = savedConfig.devices ? savedConfig : { devices: [] }
|
||||
// 确保每个设备都有 openCodePort 默认值
|
||||
configWithDevices.devices = configWithDevices.devices.map(device => ({
|
||||
...device,
|
||||
openCodePort: device.openCodePort || 3002,
|
||||
desktopPort: device.desktopPort || 3000,
|
||||
gitPort: device.gitPort || 3001,
|
||||
}))
|
||||
setConfig(configWithDevices)
|
||||
if (configWithDevices.devices.length > 0 && !selectedDeviceId) {
|
||||
setSelectedDeviceId(configWithDevices.devices[0].id)
|
||||
@@ -209,12 +217,9 @@ export const RemotePage: React.FC = () => {
|
||||
setShowConfig(true)
|
||||
return
|
||||
}
|
||||
let url = `http://${selectedConfig.serverHost}:${selectedConfig.desktopPort}/opencode`
|
||||
if (selectedConfig.password) {
|
||||
url += `?password=${encodeURIComponent(selectedConfig.password)}`
|
||||
}
|
||||
let url = `http://${selectedConfig.serverHost}:${selectedConfig.openCodePort}`
|
||||
const deviceName = selectedConfig.deviceName ? ` - ${selectedConfig.deviceName}` : ''
|
||||
const fileItem = createRemoteDesktopFileItem(url, `OpenCode${deviceName}`, selectedConfig.deviceName)
|
||||
const fileItem = createRemoteGitFileItem(url, `OpenCode${deviceName}`, selectedConfig.deviceName)
|
||||
selectFile(fileItem)
|
||||
}
|
||||
|
||||
|
||||
@@ -155,6 +155,19 @@ export const ConfigDialog: React.FC<ConfigDialogProps> = ({
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
OpenCode 端口
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
value={formData.openCodePort}
|
||||
onChange={(e) => handleChange('openCodePort', parseInt(e.target.value) || 3002)}
|
||||
placeholder="默认: 3002"
|
||||
className="w-full px-3 py-2 bg-gray-50 dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-lg text-gray-900 dark:text-gray-100 placeholder-gray-400 dark:placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-gray-900 dark:focus:ring-gray-100 focus:border-transparent transition-colors"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
访问密码
|
||||
|
||||
@@ -16,6 +16,7 @@ interface FileTransferPageProps {
|
||||
export const FileTransferPage: React.FC<FileTransferPageProps> = ({ serverHost, port, password, onClose }) => {
|
||||
const [localSelected, setLocalSelected] = useState<FileItem | null>(null)
|
||||
const [remoteSelected, setRemoteSelected] = useState<RemoteFileItem | null>(null)
|
||||
const [remotePath, setRemotePath] = useState('')
|
||||
const [transfers, setTransfers] = useState<TransferItem[]>([])
|
||||
const [transferring, setTransferring] = useState(false)
|
||||
const [transferQueueHeight, setTransferQueueHeight] = useState(128)
|
||||
@@ -42,7 +43,7 @@ export const FileTransferPage: React.FC<FileTransferPageProps> = ({ serverHost,
|
||||
const blob = await fetchSystemFileContent(localSelected.path)
|
||||
const file = new File([blob], localSelected.name, { type: blob.type })
|
||||
|
||||
await uploadFileToRemote(serverHost, port, file, '', password, (progress) => {
|
||||
await uploadFileToRemote(serverHost, port, file, remotePath, password, (progress) => {
|
||||
setTransfers((prev) =>
|
||||
prev.map((t) => (t.id === transferId ? { ...t, progress } : t))
|
||||
)
|
||||
@@ -84,7 +85,7 @@ export const FileTransferPage: React.FC<FileTransferPageProps> = ({ serverHost,
|
||||
setTransfers((prev) => [...prev, newTransfer])
|
||||
|
||||
try {
|
||||
await downloadFileFromRemote(serverHost, port, remoteSelected.name, '', password, (progress) => {
|
||||
await downloadFileFromRemote(serverHost, port, remoteSelected.name, remotePath, password, (progress) => {
|
||||
setTransfers((prev) =>
|
||||
prev.map((t) => (t.id === transferId ? { ...t, progress } : t))
|
||||
)
|
||||
@@ -166,6 +167,7 @@ export const FileTransferPage: React.FC<FileTransferPageProps> = ({ serverHost,
|
||||
selectedFile={remoteSelected}
|
||||
onSelect={setRemoteSelected}
|
||||
onDownload={handleDownload}
|
||||
onPathChange={setRemotePath}
|
||||
disabled={transferring}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -10,6 +10,7 @@ interface RemoteFilePanelProps {
|
||||
selectedFile: RemoteFileItem | null
|
||||
onSelect: (file: RemoteFileItem | null) => void
|
||||
onDownload: () => void
|
||||
onPathChange?: (path: string) => void
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
@@ -20,6 +21,7 @@ export const RemoteFilePanel: React.FC<RemoteFilePanelProps> = ({
|
||||
selectedFile,
|
||||
onSelect,
|
||||
onDownload,
|
||||
onPathChange,
|
||||
disabled,
|
||||
}) => {
|
||||
const [currentPath, setCurrentPath] = useState('')
|
||||
@@ -71,6 +73,7 @@ export const RemoteFilePanel: React.FC<RemoteFilePanelProps> = ({
|
||||
setPathHistory(newHistory)
|
||||
setCurrentPath(prevPath)
|
||||
onSelect(null)
|
||||
onPathChange?.(prevPath)
|
||||
} else {
|
||||
loadDrives()
|
||||
}
|
||||
@@ -82,11 +85,13 @@ export const RemoteFilePanel: React.FC<RemoteFilePanelProps> = ({
|
||||
setPathHistory(['', newPath])
|
||||
setCurrentPath(newPath)
|
||||
loadFiles(newPath)
|
||||
onPathChange?.(newPath)
|
||||
} else {
|
||||
const newPath = currentPath ? `${currentPath}\\${file.name}` : file.name
|
||||
setPathHistory([...pathHistory, newPath])
|
||||
setCurrentPath(newPath)
|
||||
loadFiles(newPath)
|
||||
onPathChange?.(newPath)
|
||||
}
|
||||
onSelect(null)
|
||||
}
|
||||
@@ -104,6 +109,7 @@ export const RemoteFilePanel: React.FC<RemoteFilePanelProps> = ({
|
||||
setCurrentPath('')
|
||||
loadDrives()
|
||||
onSelect(null)
|
||||
onPathChange?.('')
|
||||
}
|
||||
|
||||
const getDisplayPath = () => {
|
||||
|
||||
@@ -14,6 +14,7 @@ export interface RemoteDevice {
|
||||
serverHost: string
|
||||
desktopPort: number
|
||||
gitPort: number
|
||||
openCodePort: number
|
||||
password?: string
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user