Skip to content

Commit 400f66a

Browse files
committed
perf: sftp and tabs
1 parent 0df56b6 commit 400f66a

25 files changed

Lines changed: 1378 additions & 188 deletions

i18n/locales/en.json

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"Fonts": "Fonts",
2424
"WithSystem": "Follow system",
2525
"Appearance": "Appearance",
26+
"Settings": "Settings",
2627
"PrimaryColor": "Primary color",
2728
"LightThemePalette": "Light palette",
2829
"DarkThemePalette": "Dark palette",
@@ -100,14 +101,50 @@
100101
"RightPanel": {
101102
"Open": "Open right panel",
102103
"Close": "Close right panel",
104+
"Session": "Session",
103105
"AI": "AI",
104106
"SFTP": "SFTP",
107+
"SessionEmptyTitle": "No active session",
108+
"SessionEmptyDescription": "Connect to an SSH host from the asset tree to view session info and sharing settings here.",
109+
"SessionUnsupportedTitle": "Session not supported",
110+
"SessionUnsupportedDescription": "The session panel supports SSH terminal and SFTP sessions.",
111+
"SessionWaitingTitle": "Connecting session",
112+
"SessionWaitingDescription": "Session details and sharing will appear once the terminal is connected.",
113+
"SessionAsset": "Asset",
114+
"SessionAddress": "Address",
115+
"SessionAccount": "Account",
116+
"SessionProtocol": "Protocol",
117+
"SessionDuration": "Duration",
118+
"SessionId": "Session ID",
119+
"SessionStatusConnected": "Connected",
120+
"SessionStatusConnecting": "Connecting",
121+
"SessionStatusFailed": "Failed",
122+
"SessionStatusIdle": "Idle",
123+
"OnlineUsers": "Online users",
124+
"Share": "Share",
125+
"NoOnlineUsers": "No online users",
126+
"ShareLink": "Share link",
127+
"ShareDisabled": "Sharing is not enabled for this session",
128+
"ExpiredTime": "Expiry",
129+
"ExpiredMinutes": "{count} min",
130+
"ExpiredHour": "{count} hr",
131+
"ActionPerm": "Permission",
132+
"Writable": "Writable",
133+
"ReadOnly": "Read only",
134+
"ShareUser": "Share users",
135+
"GetShareUser": "Search and select users",
136+
"CreateLink": "Create link",
137+
"VerifyCode": "Verify code",
138+
"CopyLink": "Copy link",
139+
"Back": "Back",
140+
"PrimaryUser": "Primary",
141+
"RemoveShareUser": "Remove user",
105142
"BatchCommand": "Batch",
106143
"AIEmptyTitle": "AI assistant",
107144
"AIEmptyDescription": "Ask questions and get ops suggestions or command help.",
108145
"AIInputPlaceholder": "Describe your task or the command you want to run…",
109146
"AISend": "Send",
110-
"AIPlaceholderReply": "AI integration is coming soon. Use the SFTP and batch panels for now.",
147+
"AIPlaceholderReply": "AI integration is coming soon. Use the batch command panel for now.",
111148
"SFTPSearchPlaceholder": "Search assets for file transfer…",
112149
"SFTPEmptyTitle": "Choose an SFTP asset",
113150
"SFTPEmptyDescription": "Search for an asset or reuse the active terminal host.",
@@ -126,6 +163,21 @@
126163
"BatchCommandSentDesc": "Command dispatched to {count} terminal(s)",
127164
"BatchPanelClose": "Close batch command panel"
128165
},
166+
"FileManagement": {
167+
"ConnectRemoteSftp": "Connect remote SFTP",
168+
"RemoteSftp": "Remote SFTP",
169+
"LocalSftp": "Current SFTP",
170+
"DisconnectRemote": "Disconnect remote",
171+
"RemoteSftpHint": "Connect another SFTP server to transfer files between both sides",
172+
"RemoteConnected": "Remote SFTP connected",
173+
"RemoteConnectFailed": "Failed to connect remote SFTP",
174+
"TransferToRemote": "Send to remote",
175+
"TransferToLocal": "Send to current",
176+
"TransferSuccess": "File transferred",
177+
"TransferFailed": "File transfer failed",
178+
"FolderTransferUnsupported": "Folder transfer is not supported yet",
179+
"DownloadFolderConfirm": "Download this folder?"
180+
},
129181
"Transcode": {
130182
"Title": "Replay Transcode",
131183
"Description": "Choose one or multiple .tar recording archive files for MP4 conversion.",

i18n/locales/zh.json

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"Fonts": "字体",
2424
"WithSystem": "跟随系统",
2525
"Appearance": "外观",
26+
"Settings": "设置",
2627
"PrimaryColor": "主色",
2728
"LightThemePalette": "浅色主题",
2829
"DarkThemePalette": "深色主题",
@@ -100,14 +101,50 @@
100101
"RightPanel": {
101102
"Open": "打开右侧面板",
102103
"Close": "关闭右侧面板",
104+
"Session": "会话",
103105
"AI": "AI",
104106
"SFTP": "SFTP",
107+
"SessionEmptyTitle": "暂无活动会话",
108+
"SessionEmptyDescription": "从左侧资产树连接 SSH 主机后,可在此查看会话信息与分享设置。",
109+
"SessionUnsupportedTitle": "当前会话不支持",
110+
"SessionUnsupportedDescription": "会话面板目前支持 SSH 终端与 SFTP 会话。",
111+
"SessionWaitingTitle": "会话连接中",
112+
"SessionWaitingDescription": "终端建立连接后将显示会话详情与分享功能。",
113+
"SessionAsset": "资产",
114+
"SessionAddress": "地址",
115+
"SessionAccount": "账号",
116+
"SessionProtocol": "协议",
117+
"SessionDuration": "连接时长",
118+
"SessionId": "会话 ID",
119+
"SessionStatusConnected": "已连接",
120+
"SessionStatusConnecting": "连接中",
121+
"SessionStatusFailed": "连接失败",
122+
"SessionStatusIdle": "未连接",
123+
"OnlineUsers": "在线用户",
124+
"Share": "分享",
125+
"NoOnlineUsers": "暂无在线用户",
126+
"ShareLink": "分享链接",
127+
"ShareDisabled": "当前会话未开启分享权限",
128+
"ExpiredTime": "过期时间",
129+
"ExpiredMinutes": "{count} 分钟",
130+
"ExpiredHour": "{count} 小时",
131+
"ActionPerm": "操作权限",
132+
"Writable": "可写",
133+
"ReadOnly": "只读",
134+
"ShareUser": "分享用户",
135+
"GetShareUser": "搜索并选择用户",
136+
"CreateLink": "创建链接",
137+
"VerifyCode": "验证码",
138+
"CopyLink": "复制链接",
139+
"Back": "返回",
140+
"PrimaryUser": "主用户",
141+
"RemoveShareUser": "移除用户",
105142
"BatchCommand": "批量命令",
106143
"AIEmptyTitle": "AI 助手",
107144
"AIEmptyDescription": "输入问题,获取运维建议与命令辅助。",
108145
"AIInputPlaceholder": "描述你的问题或想执行的运维任务…",
109146
"AISend": "发送",
110-
"AIPlaceholderReply": "AI 能力接入中。当前可先使用右侧 SFTP 与批量命令面板",
147+
"AIPlaceholderReply": "AI 能力接入中。当前可先使用批量命令面板",
111148
"SFTPSearchPlaceholder": "搜索要传输文件的资产…",
112149
"SFTPEmptyTitle": "选择 SFTP 资产",
113150
"SFTPEmptyDescription": "搜索资产,或复用当前活动终端对应主机。",
@@ -126,6 +163,21 @@
126163
"BatchCommandSentDesc": "已向 {count} 个终端发送命令",
127164
"BatchPanelClose": "关闭批量命令面板"
128165
},
166+
"FileManagement": {
167+
"ConnectRemoteSftp": "连接远程 SFTP",
168+
"RemoteSftp": "远程 SFTP",
169+
"LocalSftp": "当前 SFTP",
170+
"DisconnectRemote": "断开远程",
171+
"RemoteSftpHint": "连接另一台 SFTP 服务器,可在两侧互传文件",
172+
"RemoteConnected": "远程 SFTP 已连接",
173+
"RemoteConnectFailed": "远程 SFTP 连接失败",
174+
"TransferToRemote": "传到远程",
175+
"TransferToLocal": "传到当前",
176+
"TransferSuccess": "文件传输成功",
177+
"TransferFailed": "文件传输失败",
178+
"FolderTransferUnsupported": "暂不支持文件夹互传",
179+
"DownloadFolderConfirm": "确定下载此文件夹?"
180+
},
129181
"Transcode": {
130182
"Title": "录像转码",
131183
"Description": "选择一个或多个录像压缩文件(.tar),将录像转换为 mp4 格式。",

ui/components/EditForm/editForm.vue

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -134,13 +134,17 @@ const protocolTabItems = computed(() =>
134134
);
135135
const connectMethodTypeItems = computed(() => {
136136
const metaMap: Record<string, { label: string, icon: string }> = {
137-
web: { label: "Web CLI", icon: "i-lucide-globe" },
137+
builtin: { label: "内置", icon: "i-lucide-panels-top-left" },
138138
native: { label: "客户端", icon: "i-lucide-monitor" },
139-
applet: { label: "Applet", icon: "i-lucide-box" },
140-
virtual_app: { label: "VirtualApp", icon: "i-lucide-app-window" }
139+
remote_app: { label: "远程应用", icon: "i-lucide-app-window" }
141140
};
142-
const order = ["web", "native", "applet", "virtual_app"];
143-
const grouped = new Set(availableConnectMethods.value.map((method) => String(method.type || "")));
141+
const categoryOf = (type: string) => {
142+
if (type === "web") return "builtin";
143+
if (["applet", "virtual_app"].includes(type)) return "remote_app";
144+
return type;
145+
};
146+
const order = ["builtin", "native", "remote_app"];
147+
const grouped = new Set(availableConnectMethods.value.map((method) => categoryOf(String(method.type || ""))));
144148
const sorted = [
145149
...order.filter((type) => grouped.has(type)),
146150
...Array.from(grouped).filter((type) => !order.includes(type))
@@ -156,7 +160,10 @@ const connectMethodTabItems = computed(() =>
156160
availableConnectMethods.value
157161
.filter((method) => {
158162
if (!selectedConnectMethodType.value) return true;
159-
return String(method.type || "") === selectedConnectMethodType.value;
163+
const type = String(method.type || "");
164+
if (selectedConnectMethodType.value === "builtin") return type === "web";
165+
if (selectedConnectMethodType.value === "remote_app") return ["applet", "virtual_app"].includes(type);
166+
return type === selectedConnectMethodType.value;
160167
})
161168
.map((method) => ({
162169
label: method.label || method.value,
@@ -226,12 +233,18 @@ watch(
226233
227234
const current = availableConnectMethods.value.find((method) => method.value === props.connectMethod);
228235
if (current?.type) {
229-
selectedConnectMethodType.value = String(current.type);
236+
const type = String(current.type);
237+
selectedConnectMethodType.value = type === "web"
238+
? "builtin"
239+
: ["applet", "virtual_app"].includes(type) ? "remote_app" : type;
230240
return;
231241
}
232242
233243
if (!selectedConnectMethodType.value) {
234-
selectedConnectMethodType.value = String(availableConnectMethods.value[0]?.type || "");
244+
const type = String(availableConnectMethods.value[0]?.type || "");
245+
selectedConnectMethodType.value = type === "web"
246+
? "builtin"
247+
: ["applet", "virtual_app"].includes(type) ? "remote_app" : type;
235248
}
236249
},
237250
{ deep: true, immediate: true }

ui/components/Header/ActionButtons.vue

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -62,17 +62,6 @@ const getWindowControlButtonClass = (buttonKey: string) => {
6262
}
6363
};
6464
65-
const { openSettings, warmupWebSettings } = useSettingsWindow();
66-
67-
const openSettingsWindow = async () => {
68-
if (isTauriRuntime()) {
69-
await useTauriCoreInvoke("open_settings_window");
70-
return;
71-
}
72-
73-
openSettings();
74-
};
75-
7665
const { open: rightPanelOpen, toggle: toggleRightPanel } = useRightPanel();
7766
</script>
7867

@@ -87,15 +76,6 @@ const { open: rightPanelOpen, toggle: toggleRightPanel } = useRightPanel();
8776
<UButton icon="solar:palette-linear" :title="currentThemePresetLabel" v-bind="commonButtonProps" />
8877
</UDropdownMenu>
8978

90-
<UButton
91-
icon="i-lucide-settings"
92-
:title="t('ToolTips.Settings')"
93-
v-bind="commonButtonProps"
94-
@mouseenter="warmupWebSettings"
95-
@focus="warmupWebSettings"
96-
@click="openSettingsWindow"
97-
/>
98-
9979
<Profile placement="topbar" />
10080

10181
<UButton

ui/components/RightPanel/aiPanel.vue

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,24 +79,24 @@ const sendMessage = async () => {
7979
</div>
8080

8181
<form class="shrink-0 border-t border-gray-200 p-3 dark:border-white/10" @submit.prevent="sendMessage">
82-
<div class="flex items-end gap-2">
82+
<div class="relative">
8383
<UTextarea
8484
v-model="draft"
8585
:rows="2"
8686
autoresize
8787
:maxrows="5"
8888
:placeholder="t('RightPanel.AIInputPlaceholder')"
89-
class="min-w-0 flex-1"
90-
:ui="{ base: 'text-[12px]' }"
89+
class="w-full"
90+
:ui="{ base: 'text-[12px] pb-9 pr-10' }"
9191
@keydown.enter.exact.prevent="sendMessage"
9292
/>
9393
<UButton
9494
type="submit"
9595
color="primary"
9696
variant="soft"
97-
size="sm"
97+
size="xs"
9898
icon="i-lucide-send"
99-
class="shrink-0"
99+
class="absolute bottom-1.5 right-1.5"
100100
:loading="sending"
101101
:disabled="!draft.trim()"
102102
:aria-label="t('RightPanel.AISend')"

ui/components/RightPanel/rightPanel.vue

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,38 @@
11
<script setup lang="ts">
22
import RightPanelAiPanel from "~/components/RightPanel/aiPanel.vue";
3+
import RightPanelSessionPanel from "~/components/RightPanel/sessionPanel.vue";
34
import RightPanelSftpPanel from "~/components/RightPanel/sftpPanel.vue";
45
56
const { t } = useI18n();
7+
const { activeTab: workspaceTab } = useWorkspaceTabs();
68
const { activeTab, setActiveTab } = useRightPanel();
79
8-
const tabs = computed(() => [
9-
{ value: "ai" as const, label: t("RightPanel.AI"), icon: "i-lucide-sparkles" },
10-
{ value: "sftp" as const, label: t("RightPanel.SFTP"), icon: "i-lucide-folder-symlink" }
11-
]);
10+
const showSftpTab = computed(() => workspaceTab.value?.protocol === "ssh");
1211
13-
const activePanelComponent = computed(() =>
14-
activeTab.value === "sftp" ? RightPanelSftpPanel : RightPanelAiPanel
15-
);
12+
const tabs = computed(() => {
13+
const items = [
14+
{ value: "session" as const, label: t("RightPanel.Session"), icon: "i-lucide-terminal" },
15+
{ value: "ai" as const, label: t("RightPanel.AI"), icon: "i-lucide-sparkles" }
16+
];
17+
18+
if (showSftpTab.value) {
19+
items.push({ value: "sftp" as const, label: t("RightPanel.SFTP"), icon: "i-lucide-folder-symlink" });
20+
}
21+
22+
return items;
23+
});
24+
25+
const panelComponents = {
26+
session: RightPanelSessionPanel,
27+
ai: RightPanelAiPanel,
28+
sftp: RightPanelSftpPanel
29+
} as const;
30+
31+
const activePanelComponent = computed(() => panelComponents[activeTab.value]);
32+
33+
watch(showSftpTab, (visible) => {
34+
if (!visible && activeTab.value === "sftp") setActiveTab("session");
35+
});
1636
</script>
1737

1838
<template>

0 commit comments

Comments
 (0)