Refine workspace toolbar and file distribution UI
This commit is contained in:
@@ -28,7 +28,7 @@ const diagnostics = computed(() => {
|
||||
`Version: ${appVersion.value}`,
|
||||
`First Launch: ${formatTime(productStatusStore.firstLaunchedAt)}`,
|
||||
`Connections: ${connectionsStore.connections.length}`,
|
||||
`Transfer Runs: ${transfersStore.runs.length}`,
|
||||
`文件分发记录: ${transfersStore.runs.length}`,
|
||||
`Activity Logs: ${activityLogStore.entries.length}`,
|
||||
`User Agent: ${navigator.userAgent}`,
|
||||
].join('\n')
|
||||
|
||||
@@ -140,7 +140,7 @@ function formatDuration(durationMs: number) {
|
||||
</header>
|
||||
|
||||
<div class="grid min-h-0 flex-1 gap-0 lg:grid-cols-[280px_minmax(0,1fr)]">
|
||||
<aside class="border-b border-slate-800 bg-slate-950/80 lg:border-b-0 lg:border-r">
|
||||
<aside class="flex min-h-0 flex-col border-b border-slate-800 bg-slate-950/80 lg:border-b-0 lg:border-r">
|
||||
<div class="flex items-center justify-between border-b border-slate-800 px-4 py-3">
|
||||
<div>
|
||||
<p class="text-sm font-medium text-slate-100">目标连接</p>
|
||||
@@ -155,7 +155,7 @@ function formatDuration(durationMs: number) {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="max-h-[260px] overflow-y-auto lg:max-h-none lg:h-full">
|
||||
<div class="min-h-0 overflow-y-auto max-h-[260px] lg:max-h-none lg:flex-1">
|
||||
<label
|
||||
v-for="connection in connectionsStore.connections"
|
||||
:key="connection.id"
|
||||
|
||||
@@ -791,20 +791,20 @@ const totalUploadProgress = computed(() => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-2 lg:grid-cols-[minmax(0,1fr)_18rem]">
|
||||
<div class="relative">
|
||||
<div class="flex flex-wrap items-stretch gap-2">
|
||||
<div class="relative min-w-0 flex-1 basis-64">
|
||||
<Search class="pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-slate-500" />
|
||||
<input
|
||||
ref="searchInputRef"
|
||||
v-model="searchQuery"
|
||||
type="text"
|
||||
placeholder="搜索文件... (Ctrl/Cmd+F)"
|
||||
class="w-full rounded-lg border border-slate-700 bg-slate-900 py-2 pl-9 pr-9 text-sm text-slate-100 placeholder:text-slate-500 focus:border-cyan-500 focus:outline-none"
|
||||
class="h-10 w-full rounded-lg border border-slate-700 bg-slate-900 py-2 pl-9 pr-9 text-sm text-slate-100 placeholder:text-slate-500 focus:border-cyan-500 focus:outline-none"
|
||||
/>
|
||||
<button
|
||||
v-if="searchQuery"
|
||||
type="button"
|
||||
class="absolute right-2 top-1/2 inline-flex h-7 w-7 -translate-y-1/2 items-center justify-center rounded text-slate-500 transition-colors hover:bg-slate-800 hover:text-slate-300"
|
||||
class="absolute right-2 top-1/2 inline-flex h-8 w-8 -translate-y-1/2 items-center justify-center rounded text-slate-500 transition-colors hover:bg-slate-800 hover:text-slate-300"
|
||||
@click="searchQuery = ''"
|
||||
aria-label="清除搜索"
|
||||
>
|
||||
@@ -812,7 +812,7 @@ const totalUploadProgress = computed(() => {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2 rounded-lg border border-slate-700 bg-slate-900 px-2 py-1.5">
|
||||
<div class="flex h-10 min-w-0 flex-1 basis-72 items-center gap-2 rounded-lg border border-slate-700 bg-slate-900 px-2">
|
||||
<input
|
||||
ref="pathInputRef"
|
||||
v-model="pathDraft"
|
||||
@@ -823,7 +823,7 @@ const totalUploadProgress = computed(() => {
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex min-h-[36px] items-center rounded-md bg-slate-800 px-3 text-xs text-slate-200 transition-colors hover:bg-slate-700"
|
||||
class="inline-flex h-8 shrink-0 items-center rounded-md bg-slate-800 px-3 text-xs text-slate-200 transition-colors hover:bg-slate-700"
|
||||
@click="navigateToTypedPath"
|
||||
>
|
||||
前往
|
||||
|
||||
@@ -88,6 +88,14 @@ const contextMenuY = ref(0)
|
||||
const contextWorkspaceId = ref<string | null>(null)
|
||||
const backupFileInput = ref<HTMLInputElement | null>(null)
|
||||
const backupBusy = ref(false)
|
||||
const workspaceTabCount = computed(() => workspaceTabs.value.length)
|
||||
|
||||
const commandGroupClass = 'flex min-w-max items-center gap-1 rounded-2xl border border-slate-800/80 bg-slate-900/80 px-1.5 py-1 shadow-[inset_0_1px_0_rgba(148,163,184,0.04)]'
|
||||
const commandButtonClass = 'inline-flex h-10 shrink-0 items-center gap-2 rounded-xl px-3 text-[13px] font-medium transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-cyan-400/60 disabled:cursor-not-allowed disabled:opacity-45'
|
||||
const commandButtonNeutralClass = 'text-slate-300 hover:bg-slate-800/85 hover:text-slate-50'
|
||||
const commandButtonEmphasisClass = 'border border-slate-700/80 bg-slate-800/90 text-slate-100 shadow-[inset_0_1px_0_rgba(148,163,184,0.05)] hover:border-slate-600/80 hover:bg-slate-800'
|
||||
const commandButtonActiveClass = 'border border-cyan-500/30 bg-cyan-500/12 text-cyan-100 shadow-[inset_0_1px_0_rgba(103,232,249,0.12)] hover:border-cyan-400/40 hover:bg-cyan-500/16'
|
||||
const utilityButtonClass = 'inline-flex h-10 min-w-[40px] items-center justify-center rounded-xl text-slate-400 transition-colors duration-200 hover:bg-slate-800/90 hover:text-slate-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-cyan-400/60'
|
||||
|
||||
function activateTab(workspaceId: string) {
|
||||
workspaceStore.activateWorkspace(workspaceId)
|
||||
@@ -268,13 +276,13 @@ const tabContextMenuItems = computed<ContextMenuItem[]>(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="border-b border-slate-700 bg-slate-900">
|
||||
<div class="border-b border-slate-800/80 px-3 py-2 sm:px-4">
|
||||
<div class="flex flex-wrap items-center gap-2 lg:flex-nowrap lg:gap-3">
|
||||
<div class="flex min-w-0 flex-1 items-center gap-2">
|
||||
<div class="border-b border-slate-800 bg-[radial-gradient(circle_at_top_left,_rgba(34,211,238,0.08),_transparent_30%),linear-gradient(180deg,_rgba(15,23,42,0.98),_rgba(2,6,23,0.98))]">
|
||||
<div class="border-b border-slate-800/80 px-3 py-3 sm:px-4">
|
||||
<div class="flex flex-wrap items-start gap-3 lg:flex-nowrap">
|
||||
<div class="flex min-w-0 flex-1 items-start gap-2">
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex min-h-[40px] min-w-[40px] shrink-0 items-center justify-center rounded-lg border border-slate-700 bg-slate-800 text-slate-300 transition-colors hover:border-slate-600 hover:text-slate-100 lg:hidden"
|
||||
class="inline-flex h-11 min-w-[44px] shrink-0 items-center justify-center rounded-xl border border-slate-800 bg-slate-900/85 text-slate-300 transition-colors duration-200 hover:border-slate-700 hover:bg-slate-800/90 hover:text-slate-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-cyan-400/60 lg:hidden"
|
||||
:aria-label="props.sidebarOpen ? '收起会话树' : '打开会话树'"
|
||||
@click="emit('toggleSidebar')"
|
||||
>
|
||||
@@ -282,120 +290,118 @@ const tabContextMenuItems = computed<ContextMenuItem[]>(() => {
|
||||
<PanelLeftOpen v-else class="h-4 w-4" />
|
||||
</button>
|
||||
|
||||
<div class="min-w-0 flex-1 overflow-x-auto pb-1 lg:pb-0 scrollbar-thin">
|
||||
<div class="flex min-w-max items-center gap-2 text-xs text-slate-300">
|
||||
<button
|
||||
type="button"
|
||||
@click="openBatchCommand"
|
||||
class="inline-flex min-h-[40px] items-center gap-1.5 rounded-lg border border-slate-700 bg-slate-800 px-3 py-2 transition-colors hover:border-slate-600 hover:text-slate-100"
|
||||
aria-label="批量命令执行"
|
||||
>
|
||||
<TerminalSquare class="h-3.5 w-3.5" />
|
||||
<span>批量命令</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@click="openOperationsHistory"
|
||||
class="inline-flex min-h-[40px] items-center gap-1.5 rounded-lg border border-slate-700 bg-slate-800 px-3 py-2 transition-colors hover:border-slate-600 hover:text-slate-100"
|
||||
aria-label="传输历史与操作日志"
|
||||
>
|
||||
<ClipboardList class="h-3.5 w-3.5" />
|
||||
<span>历史日志</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@click="handleExportBackup"
|
||||
:disabled="backupBusy"
|
||||
class="inline-flex min-h-[40px] items-center gap-1.5 rounded-lg border border-slate-700 bg-slate-800 px-3 py-2 transition-colors hover:border-slate-600 hover:text-slate-100 disabled:cursor-not-allowed disabled:opacity-50"
|
||||
aria-label="导出备份"
|
||||
>
|
||||
<Download class="h-3.5 w-3.5" />
|
||||
<span>导出备份</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@click="triggerImportBackup"
|
||||
:disabled="backupBusy"
|
||||
class="inline-flex min-h-[40px] items-center gap-1.5 rounded-lg border border-slate-700 bg-slate-800 px-3 py-2 transition-colors hover:border-slate-600 hover:text-slate-100 disabled:cursor-not-allowed disabled:opacity-50"
|
||||
aria-label="导入备份"
|
||||
>
|
||||
<Upload class="h-3.5 w-3.5" />
|
||||
<span>导入备份</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@click="openTransfers"
|
||||
class="inline-flex min-h-[40px] items-center gap-1.5 rounded-lg border px-3 py-2 transition-colors"
|
||||
:class="workspaceStore.transfersModalOpen
|
||||
? 'border-cyan-500/30 bg-cyan-500/10 text-cyan-200'
|
||||
: 'border-slate-700 bg-slate-800 hover:border-slate-600 hover:text-slate-100'"
|
||||
aria-label="打开传输页面"
|
||||
>
|
||||
<ArrowLeftRight class="h-3.5 w-3.5" />
|
||||
<span>Transfers</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@click="openCreateSession"
|
||||
class="inline-flex min-h-[40px] items-center gap-1.5 rounded-lg border border-slate-700 bg-slate-800 px-3 py-2 transition-colors hover:border-slate-600 hover:text-slate-100"
|
||||
aria-label="新增会话"
|
||||
>
|
||||
<Plus class="h-3.5 w-3.5" />
|
||||
<span>新增连接</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@click="duplicateActiveWorkspace"
|
||||
:disabled="!activeWorkspace"
|
||||
class="inline-flex min-h-[40px] items-center gap-1.5 rounded-lg border border-slate-700 bg-slate-800 px-3 py-2 transition-colors hover:border-slate-600 hover:text-slate-100 disabled:cursor-not-allowed disabled:opacity-50"
|
||||
aria-label="复制当前工作区"
|
||||
>
|
||||
<CopyPlus class="h-3.5 w-3.5" />
|
||||
<span>复制会话</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@click="toggleTerminal"
|
||||
:disabled="!activeWorkspace"
|
||||
class="inline-flex min-h-[40px] items-center gap-1.5 rounded-lg border px-3 py-2 transition-colors disabled:cursor-not-allowed disabled:opacity-50"
|
||||
:class="activeWorkspace?.terminalVisible
|
||||
? 'border-cyan-500/30 bg-cyan-500/10 text-cyan-200'
|
||||
: 'border-slate-700 bg-slate-800 hover:border-slate-600 hover:text-slate-100'"
|
||||
aria-label="切换终端面板"
|
||||
>
|
||||
<SquareTerminal class="h-3.5 w-3.5" />
|
||||
<span>终端</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@click="toggleSftp"
|
||||
:disabled="!activeWorkspace"
|
||||
class="inline-flex min-h-[40px] items-center gap-1.5 rounded-lg border px-3 py-2 transition-colors disabled:cursor-not-allowed disabled:opacity-50"
|
||||
:class="activeWorkspace?.sftpVisible
|
||||
? 'border-cyan-500/30 bg-cyan-500/10 text-cyan-200'
|
||||
: 'border-slate-700 bg-slate-800 hover:border-slate-600 hover:text-slate-100'"
|
||||
aria-label="切换 SFTP 面板"
|
||||
>
|
||||
<FolderTree class="h-3.5 w-3.5" />
|
||||
<span>文件</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@click="resetSplit"
|
||||
:disabled="!activeWorkspace"
|
||||
class="inline-flex min-h-[40px] items-center gap-1.5 rounded-lg border border-slate-700 bg-slate-800 px-3 py-2 transition-colors hover:border-slate-600 hover:text-slate-100 disabled:cursor-not-allowed disabled:opacity-50"
|
||||
aria-label="重置分屏比例"
|
||||
>
|
||||
<Columns2 class="h-3.5 w-3.5" />
|
||||
<span>重置分屏</span>
|
||||
</button>
|
||||
<div class="min-w-0 flex-1 overflow-x-auto pb-1 scrollbar-thin">
|
||||
<div class="flex min-w-max items-start gap-2.5">
|
||||
<div :class="commandGroupClass">
|
||||
<div class="hidden px-2 text-[11px] font-medium text-slate-500 xl:block">全局</div>
|
||||
<button
|
||||
type="button"
|
||||
@click="openBatchCommand"
|
||||
:class="[commandButtonClass, commandButtonNeutralClass]"
|
||||
aria-label="批量命令执行"
|
||||
>
|
||||
<TerminalSquare class="h-3.5 w-3.5" />
|
||||
<span>批量命令</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@click="openOperationsHistory"
|
||||
:class="[commandButtonClass, commandButtonNeutralClass]"
|
||||
aria-label="传输历史与操作日志"
|
||||
>
|
||||
<ClipboardList class="h-3.5 w-3.5" />
|
||||
<span>历史日志</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@click="handleExportBackup"
|
||||
:disabled="backupBusy"
|
||||
:class="[commandButtonClass, commandButtonNeutralClass]"
|
||||
aria-label="导出备份"
|
||||
>
|
||||
<Download class="h-3.5 w-3.5" />
|
||||
<span>导出备份</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@click="triggerImportBackup"
|
||||
:disabled="backupBusy"
|
||||
:class="[commandButtonClass, commandButtonNeutralClass]"
|
||||
aria-label="导入备份"
|
||||
>
|
||||
<Upload class="h-3.5 w-3.5" />
|
||||
<span>导入备份</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@click="openTransfers"
|
||||
:class="[commandButtonClass, workspaceStore.transfersModalOpen ? commandButtonActiveClass : commandButtonNeutralClass]"
|
||||
aria-label="打开文件分发"
|
||||
>
|
||||
<ArrowLeftRight class="h-3.5 w-3.5" />
|
||||
<span>文件分发</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div :class="commandGroupClass">
|
||||
<div class="hidden px-2 text-[11px] font-medium text-slate-500 xl:block">工作区</div>
|
||||
<button
|
||||
type="button"
|
||||
@click="openCreateSession"
|
||||
:class="[commandButtonClass, commandButtonEmphasisClass]"
|
||||
aria-label="新增会话"
|
||||
>
|
||||
<Plus class="h-3.5 w-3.5" />
|
||||
<span>新增连接</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@click="duplicateActiveWorkspace"
|
||||
:disabled="!activeWorkspace"
|
||||
:class="[commandButtonClass, commandButtonNeutralClass]"
|
||||
aria-label="复制当前工作区"
|
||||
>
|
||||
<CopyPlus class="h-3.5 w-3.5" />
|
||||
<span>复制会话</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@click="toggleTerminal"
|
||||
:disabled="!activeWorkspace"
|
||||
:class="[commandButtonClass, activeWorkspace?.terminalVisible ? commandButtonActiveClass : commandButtonNeutralClass]"
|
||||
aria-label="切换终端面板"
|
||||
>
|
||||
<SquareTerminal class="h-3.5 w-3.5" />
|
||||
<span>终端</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@click="toggleSftp"
|
||||
:disabled="!activeWorkspace"
|
||||
:class="[commandButtonClass, activeWorkspace?.sftpVisible ? commandButtonActiveClass : commandButtonNeutralClass]"
|
||||
aria-label="切换 SFTP 面板"
|
||||
>
|
||||
<FolderTree class="h-3.5 w-3.5" />
|
||||
<span>文件</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@click="resetSplit"
|
||||
:disabled="!activeWorkspace"
|
||||
:class="[commandButtonClass, commandButtonNeutralClass]"
|
||||
aria-label="重置分屏比例"
|
||||
>
|
||||
<Columns2 class="h-3.5 w-3.5" />
|
||||
<span>重置分屏</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ml-auto flex shrink-0 items-center gap-1 sm:gap-2">
|
||||
<div class="ml-auto flex shrink-0 items-center gap-1 rounded-2xl border border-slate-800/80 bg-slate-950/70 px-1.5 py-1">
|
||||
<button
|
||||
class="inline-flex min-h-[40px] min-w-[40px] items-center justify-center rounded-lg text-slate-400 transition-colors hover:bg-slate-800 hover:text-slate-200"
|
||||
:class="utilityButtonClass"
|
||||
title="通知"
|
||||
type="button"
|
||||
>
|
||||
@@ -403,7 +409,7 @@ const tabContextMenuItems = computed<ContextMenuItem[]>(() => {
|
||||
</button>
|
||||
<button
|
||||
@click="openAbout"
|
||||
class="hidden min-h-[40px] min-w-[40px] items-center justify-center rounded-lg text-slate-400 transition-colors hover:bg-slate-800 hover:text-slate-200 sm:inline-flex"
|
||||
:class="[utilityButtonClass, 'hidden sm:inline-flex']"
|
||||
title="关于与诊断"
|
||||
type="button"
|
||||
>
|
||||
@@ -411,7 +417,7 @@ const tabContextMenuItems = computed<ContextMenuItem[]>(() => {
|
||||
</button>
|
||||
<button
|
||||
@click="openSettings"
|
||||
class="inline-flex min-h-[40px] min-w-[40px] items-center justify-center rounded-lg text-slate-400 transition-colors hover:bg-slate-800 hover:text-slate-200"
|
||||
:class="utilityButtonClass"
|
||||
title="设置中心"
|
||||
aria-label="设置中心"
|
||||
type="button"
|
||||
@@ -420,50 +426,52 @@ const tabContextMenuItems = computed<ContextMenuItem[]>(() => {
|
||||
</button>
|
||||
<button
|
||||
@click="openChangePassword"
|
||||
class="inline-flex min-h-[40px] min-w-[40px] items-center justify-center rounded-lg text-slate-400 transition-colors hover:bg-slate-800 hover:text-slate-200"
|
||||
:class="utilityButtonClass"
|
||||
title="修改密码"
|
||||
aria-label="修改密码"
|
||||
type="button"
|
||||
>
|
||||
<ShieldCheck class="h-4 w-4" />
|
||||
</button>
|
||||
<div class="mx-1 hidden h-6 w-px bg-slate-800 sm:block" />
|
||||
<button
|
||||
@click="handleLogout"
|
||||
class="inline-flex min-h-[40px] items-center gap-1.5 rounded-lg px-2.5 py-2 text-slate-400 transition-colors hover:bg-slate-800 hover:text-slate-200"
|
||||
class="inline-flex h-10 items-center gap-1.5 rounded-xl px-3 text-[13px] font-medium text-slate-300 transition-colors duration-200 hover:bg-slate-800/90 hover:text-slate-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-cyan-400/60"
|
||||
title="退出登录"
|
||||
aria-label="退出登录"
|
||||
type="button"
|
||||
>
|
||||
<LogOut class="h-4 w-4" />
|
||||
<span class="hidden text-xs sm:inline">退出</span>
|
||||
<span class="hidden sm:inline">退出</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-slate-950/35 px-3 py-2 sm:px-4">
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="hidden shrink-0 pt-2 sm:block">
|
||||
<p class="text-[11px] font-medium uppercase tracking-[0.18em] text-slate-500">会话</p>
|
||||
<div class="border-t border-slate-900/70 bg-slate-950/55 px-3 pt-2 sm:px-4">
|
||||
<div class="flex items-end gap-3">
|
||||
<div class="hidden shrink-0 pb-3 sm:block">
|
||||
<p class="text-[11px] font-medium text-slate-400">工作区</p>
|
||||
<p class="mt-1 text-xs text-slate-500">{{ workspaceTabCount }} 个打开</p>
|
||||
</div>
|
||||
|
||||
<div class="min-w-0 flex-1">
|
||||
<div v-if="workspaceTabs.length > 0" class="flex items-center gap-1 overflow-x-auto pb-1 scrollbar-thin">
|
||||
<div v-if="workspaceTabs.length > 0" class="flex items-end gap-2 overflow-x-auto scrollbar-thin">
|
||||
<div
|
||||
v-for="tab in workspaceTabs"
|
||||
:key="tab.workspaceId"
|
||||
class="group -mb-px flex min-h-[38px] max-w-[280px] shrink-0 items-center gap-2 rounded-t-lg border border-b-0 px-3 text-xs transition-colors cursor-pointer"
|
||||
class="group relative flex max-w-[300px] shrink-0 cursor-pointer items-center gap-2 rounded-t-2xl border px-3.5 transition-all duration-200"
|
||||
:class="tab.active
|
||||
? 'border-cyan-400/35 bg-slate-900 text-cyan-100 shadow-[0_-1px_0_rgba(34,211,238,0.16)]'
|
||||
: 'border-slate-700/80 bg-slate-800/65 text-slate-400 hover:bg-slate-800 hover:text-slate-100'"
|
||||
? 'z-10 h-11 border-slate-700 border-b-slate-950 bg-slate-950 text-slate-50 shadow-[0_-10px_30px_rgba(15,23,42,0.3),inset_0_1px_0_rgba(148,163,184,0.08)]'
|
||||
: 'mt-2 h-9 border-transparent bg-slate-900/35 text-slate-500 hover:mt-1 hover:h-10 hover:bg-slate-900/60 hover:text-slate-200'"
|
||||
@click="activateTab(tab.workspaceId)"
|
||||
@contextmenu="(e) => openTabContextMenu(tab.workspaceId, e)"
|
||||
>
|
||||
<span class="h-2 w-2 rounded-full" :class="tab.active ? 'bg-cyan-400' : 'bg-slate-500'" />
|
||||
<span class="truncate max-w-[220px]">{{ tab.title }}</span>
|
||||
<span class="h-2 w-2 rounded-full" :class="tab.active ? 'bg-cyan-400 shadow-[0_0_0_4px_rgba(34,211,238,0.12)]' : 'bg-slate-600'" />
|
||||
<span class="truncate max-w-[220px] text-[13px] font-medium">{{ tab.title }}</span>
|
||||
<button
|
||||
type="button"
|
||||
class="rounded p-0.5 text-slate-500 opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100 hover:bg-slate-700 hover:text-slate-200"
|
||||
class="rounded-lg p-1 text-slate-500 opacity-0 transition-all duration-200 group-hover:opacity-100 group-focus-within:opacity-100 hover:bg-slate-800 hover:text-slate-200 focus-visible:opacity-100 focus-visible:outline-none"
|
||||
@click="(e) => closeTab(tab.workspaceId, e)"
|
||||
:aria-label="`关闭会话 ${tab.title}`"
|
||||
>
|
||||
@@ -471,7 +479,9 @@ const tabContextMenuItems = computed<ContextMenuItem[]>(() => {
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="py-2 text-xs text-slate-500">未打开工作区。点击左侧连接可创建新实例。</div>
|
||||
<div v-else class="flex h-11 items-center rounded-t-2xl border border-dashed border-slate-800 bg-slate-950/45 px-4 text-sm text-slate-500">
|
||||
未打开工作区。点击左侧连接可创建新实例。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -379,17 +379,17 @@ function resolveErrorMessage(error: unknown, fallback: string) {
|
||||
class="w-full max-w-[1240px] h-[88vh] rounded-2xl border border-slate-800 bg-slate-900 shadow-2xl flex flex-col overflow-hidden"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-label="Transfers 弹窗"
|
||||
aria-label="文件分发弹窗"
|
||||
>
|
||||
<header class="h-12 px-4 border-b border-slate-800 flex items-center justify-between shrink-0">
|
||||
<div class="inline-flex items-center gap-2 text-sm text-slate-200">
|
||||
<ArrowLeftRight class="w-4 h-4 text-cyan-300" />
|
||||
<span class="font-medium">Transfers</span>
|
||||
<span class="font-medium">文件分发</span>
|
||||
</div>
|
||||
<button
|
||||
@click="closeTransfersModal"
|
||||
class="w-9 h-9 rounded-md border border-slate-700 text-slate-300 hover:bg-slate-800 hover:text-slate-100 transition-colors cursor-pointer"
|
||||
aria-label="关闭 Transfers 面板"
|
||||
aria-label="关闭文件分发面板"
|
||||
>
|
||||
<X class="w-4 h-4 mx-auto" />
|
||||
</button>
|
||||
|
||||
@@ -255,7 +255,7 @@ onMounted(async () => {
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div class="min-w-0">
|
||||
<h1 class="font-semibold tracking-tight text-slate-50" :class="props.embedded ? 'text-xl' : 'text-2xl'">Transfers</h1>
|
||||
<h1 class="font-semibold tracking-tight text-slate-50" :class="props.embedded ? 'text-xl' : 'text-2xl'">文件分发</h1>
|
||||
<p class="text-sm text-slate-400">本机 -> 多台 / 其他机器 -> 多台</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 self-start sm:self-auto">
|
||||
|
||||
Reference in New Issue
Block a user