<template>
  <div class="desktop-workbench" @click="closeWorkspaceTabMenu">
    <aside class="desktop-sidebar">
      <header class="sidebar-head">
        <div class="sidebar-title-row">
          <div class="sidebar-app-brand">
            <img class="sidebar-app-icon" src="/icons/ctms-icon-192.png" alt="" aria-hidden="true" />
            <h1>{{ TEXT.common.appName }}</h1>
          </div>

          <button v-if="showSidebarProjectChooser" class="study-switcher-trigger empty" type="button" @click="openDesktopProjectEntry">
            选择项目
          </button>
        </div>
      </header>

      <div class="sidebar-scroll">
        <section v-if="desktopFavoriteRoutes.length" class="nav-section">
          <div class="section-label">收藏</div>
          <button
            v-for="item in desktopFavoriteRoutes"
            :key="`favorite:${item.path}`"
            type="button"
            class="sidebar-link compact"
            :class="{ active: activeMenu === item.path }"
            @click="router.push(item.path)"
          >
            <el-icon><StarFilled /></el-icon>
            <span>{{ item.title }}</span>
          </button>
        </section>

        <section v-if="adminNavigationItems.length" class="nav-section">
          <div class="section-label">{{ DESKTOP_ADMIN_SECTION_LABEL }}</div>
          <template v-for="item in adminNavigationItems" :key="item.path">
            <button
              v-if="!item.children?.length"
              type="button"
              class="sidebar-link"
              :class="{ active: activeMenu === item.path }"
              @click="router.push(item.path)"
            >
              <el-icon><component :is="iconFor(item.icon)" /></el-icon>
              <span>{{ item.label }}</span>
            </button>
            <div v-else class="sidebar-group" :class="{ expanded: isNavigationGroupExpanded(item) }">
              <button
                type="button"
                class="sidebar-link group-title"
                :class="{ active: isNavigationGroupActive(item) }"
                :aria-expanded="isNavigationGroupExpanded(item)"
                @click="toggleNavigationGroup(item)"
              >
                <el-icon><component :is="iconFor(item.icon)" /></el-icon>
                <span>{{ item.label }}</span>
                <el-icon class="group-expander"><ArrowDown /></el-icon>
              </button>
              <div v-show="isNavigationGroupExpanded(item)" class="sidebar-children">
                <button
                  v-for="child in item.children"
                  :key="child.path"
                  type="button"
                  class="sidebar-link child"
                  :class="{ active: activeMenu === child.path }"
                  @click="router.push(child.path)"
                >
                  <span>{{ child.label }}</span>
                </button>
              </div>
            </div>
          </template>
        </section>

        <section v-if="projectNavigationItems.length" class="nav-section">
          <div class="section-label">{{ desktopProjectSectionLabel }}</div>
          <template v-for="item in projectNavigationItems" :key="item.path">
            <button
              v-if="!item.children?.length"
              type="button"
              class="sidebar-link"
              :class="{ active: activeMenu === item.path }"
              @click="router.push(item.path)"
            >
              <el-icon><component :is="iconFor(item.icon)" /></el-icon>
              <span>{{ item.label }}</span>
            </button>
            <div v-else class="sidebar-group" :class="{ expanded: isNavigationGroupExpanded(item) }">
              <button
                type="button"
                class="sidebar-link group-title"
                :class="{ active: isNavigationGroupActive(item) }"
                :aria-expanded="isNavigationGroupExpanded(item)"
                @click="toggleNavigationGroup(item)"
              >
                <el-icon><component :is="iconFor(item.icon)" /></el-icon>
                <span>{{ item.label }}</span>
                <el-icon class="group-expander"><ArrowDown /></el-icon>
              </button>
              <div v-show="isNavigationGroupExpanded(item)" class="sidebar-children">
                <button
                  v-for="child in item.children"
                  :key="child.path"
                  type="button"
                  class="sidebar-link child"
                  :class="{ active: activeMenu === child.path }"
                  @click="router.push(child.path)"
                >
                  <span>{{ child.label }}</span>
                </button>
              </div>
            </div>
          </template>
        </section>
      </div>
    </aside>

    <section class="desktop-main">
      <header class="desktop-toolbar">
        <div class="toolbar-left" data-tauri-drag-region>
          <div class="toolbar-context" data-tauri-drag-region>
            <div class="toolbar-title" data-tauri-drag-region>{{ desktopToolbarTitle }}</div>
            <div class="toolbar-subtitle" data-tauri-drag-region>{{ desktopToolbarSubtitle }}</div>
          </div>
        </div>

        <div class="toolbar-right">
          <button v-if="desktopUpdateNoticeVisible" class="update-notice" type="button" title="打开系统偏好安装更新" @click="openDesktopPreferences">
            <el-icon><Download /></el-icon>
            <span>{{ desktopUpdateNoticeLabel }}</span>
          </button>

          <el-popover placement="bottom-end" width="340" trigger="click" popper-class="desktop-activity-popover">
            <template #reference>
              <button class="icon-button activity-button" type="button" title="活动">
                <el-badge v-if="desktopActivityBadgeCount > 0" :value="desktopActivityBadgeValue" class="activity-badge">
                  <el-icon><Clock /></el-icon>
                </el-badge>
                <el-icon v-else><Clock /></el-icon>
              </button>
            </template>
            <div class="activity-panel">
              <div class="activity-head">
                <div>
                  <div class="panel-title">活动</div>
                  <div class="panel-subtitle">上传、下载、导出与更新</div>
                </div>
                <button
                  v-if="hasFinishedDesktopActivities"
                  class="activity-clear"
                  type="button"
                  @click="clearFinishedDesktopActivities"
                >
                  清除已结束
                </button>
              </div>
              <div v-if="desktopActivities.length" class="activity-list">
                <div v-for="item in desktopActivities" :key="item.id" class="activity-item" :class="`is-${item.status}`">
                  <span class="activity-mark" :class="`is-${item.status}`"></span>
                  <span class="activity-body">
                    <span class="activity-title">{{ item.title }}</span>
                    <span class="activity-detail">{{ item.detail || desktopActivityStatusLabel(item.status) }}</span>
                  </span>
                  <span class="activity-meta">
                    <span v-if="item.status === 'running' && item.progress !== null">{{ item.progress }}%</span>
                    <span v-else>{{ desktopActivityStatusLabel(item.status) }}</span>
                    <small>{{ formatDesktopActivityTime(item.updatedAt) }}</small>
                  </span>
                </div>
              </div>
              <div v-else class="activity-empty">暂无活动</div>
            </div>
          </el-popover>

          <button class="command-trigger" type="button" @click="openCommandPalette">
            <el-icon><Search /></el-icon>
            <span>命令</span>
            <kbd>⌘K</kbd>
          </button>

          <button class="icon-button" type="button" :title="currentRouteFavorited ? '取消收藏当前模块' : '收藏当前模块'" @click="toggleCurrentFavorite">
            <el-icon><StarFilled v-if="currentRouteFavorited" /><Star v-else /></el-icon>
          </button>

          <el-popover placement="bottom-end" width="320" trigger="click" popper-class="desktop-connection-popover">
            <template #reference>
              <button class="connection-button" type="button">
                <span class="connection-dot" :class="desktopConnectionClass"></span>
                <span>{{ desktopConnectionLabel }}</span>
              </button>
            </template>
            <div class="connection-panel">
              <div class="panel-title">在线桌面客户端</div>
              <div class="panel-row">
                <span>服务器</span>
                <code>{{ desktopServerUrl || "未配置" }}</code>
              </div>
              <div class="panel-row">
                <span>客户端</span>
                <code>{{ desktopMetadata.version }} / {{ desktopMetadata.platform }}</code>
              </div>
              <el-button size="small" @click="openDesktopPreferences">系统偏好</el-button>
            </div>
          </el-popover>

          <el-dropdown trigger="click" placement="bottom-end" @command="handleReminderCommand">
            <button class="icon-button reminder-button" type="button" title="项目提醒">
              <el-badge v-if="headerReminderTotal > 0" :value="headerReminderBadgeValue" class="reminder-badge">
                <el-icon><Bell /></el-icon>
              </el-badge>
              <el-icon v-else><Bell /></el-icon>
            </button>
            <template #dropdown>
              <el-dropdown-menu class="reminder-menu">
                <div class="reminder-panel">
                  <div class="reminder-head">
                    <div>
                      <div class="panel-title">{{ TEXT.common.labels.projectReminders }}</div>
                      <div class="panel-subtitle">{{ TEXT.common.labels.projectRemindersSubtitle }}</div>
                    </div>
                    <span v-if="headerReminderTotal > 0" class="reminder-total">{{ headerReminderTotal }}</span>
                  </div>
                  <template v-if="headerReminderItems.length">
                    <el-dropdown-item
                      v-for="item in headerReminderItems"
                      :key="item.key"
                      :command="item.command"
                      class="reminder-item"
                    >
                      <span class="reminder-mark" :class="`is-${item.tone}`"></span>
                      <span class="reminder-body">
                        <span>{{ item.title }}</span>
                        <small>{{ item.description }}</small>
                      </span>
                      <strong>{{ item.count }}</strong>
                    </el-dropdown-item>
                  </template>
                  <div v-else class="reminder-empty">
                    {{ headerRemindersLoading ? TEXT.common.loading : TEXT.common.labels.projectRemindersEmpty }}
                  </div>
                </div>
              </el-dropdown-menu>
            </template>
          </el-dropdown>

          <el-dropdown trigger="click" placement="bottom-end" @command="onCommand">
            <button class="account-button" type="button">
              <span class="account-avatar">{{ userDisplayInitial }}</span>
              <span class="account-name">{{ userDisplayName }}</span>
              <el-icon><ArrowDown /></el-icon>
            </button>
            <template #dropdown>
              <el-dropdown-menu>
                <el-dropdown-item command="profile">
                  <el-icon><User /></el-icon>
                  <span>{{ TEXT.menu.profile }}</span>
                </el-dropdown-item>
                <el-dropdown-item command="projectEntry">
                  <el-icon><Suitcase /></el-icon>
                  <span>{{ projectEntryMenuLabel }}</span>
                </el-dropdown-item>
                <el-dropdown-item command="desktopPreferences">
                  <el-icon><Monitor /></el-icon>
                  <span>系统偏好</span>
                </el-dropdown-item>
                <el-dropdown-item command="logout" :disabled="loggingOut" divided>
                  <el-icon><SwitchButton /></el-icon>
                  <span>{{ loggingOut ? "正在退出..." : TEXT.menu.logout }}</span>
                </el-dropdown-item>
              </el-dropdown-menu>
            </template>
          </el-dropdown>
        </div>
      </header>

      <div v-if="workspaceTabs.length" ref="workspaceTabsElement" class="workspace-tabs">
        <div
          class="workspace-tabs-list"
          :style="{ gridTemplateColumns: `repeat(${visibleWorkspaceTabs.length}, minmax(0, 238px))` }"
        >
          <div
            v-for="item in visibleWorkspaceTabs"
            :key="item.path"
            class="workspace-tab"
            :class="{ active: activeMenu === item.path, dragging: draggingWorkspaceTabPath === item.path }"
            draggable="true"
            @dragstart="startWorkspaceTabDrag(item.path, $event)"
            @dragover="allowWorkspaceTabDrop"
            @drop.prevent="dropWorkspaceTab(item.path, $event)"
            @dragend="draggingWorkspaceTabPath = ''"
            @contextmenu.prevent.stop="openWorkspaceTabMenu(item, $event)"
          >
            <button
              class="workspace-tab-action"
              type="button"
              :title="item.title"
              @click="navigateWorkspaceTab(item.path)"
            >
              <span>{{ item.title }}</span>
            </button>
            <button class="tab-close" type="button" title="关闭标签" @click.stop="closeWorkspaceTab(item.path)">
              ×
            </button>
          </div>
        </div>

        <el-dropdown
          v-if="overflowWorkspaceTabs.length"
          class="workspace-tabs-overflow"
          popper-class="workspace-tabs-overflow-popper"
          trigger="click"
          placement="bottom-end"
          @command="navigateOverflowWorkspaceTab"
          @visible-change="workspaceTabsOverflowOpen = $event"
        >
          <button
            class="workspace-tabs-overflow-trigger"
            :class="{ 'is-open': workspaceTabsOverflowOpen }"
            type="button"
            :aria-label="`查看其余 ${overflowWorkspaceTabs.length} 个标签`"
          >
            <span>更多</span>
            <span class="workspace-tabs-overflow-count">{{ overflowWorkspaceTabs.length }}</span>
            <el-icon><ArrowDown /></el-icon>
          </button>
          <template #dropdown>
            <div class="workspace-tabs-overflow-menu">
              <div class="workspace-tabs-overflow-header">
                <span>其他标签</span>
                <span>{{ overflowWorkspaceTabs.length }} 个</span>
              </div>
              <el-dropdown-menu>
                <el-dropdown-item
                  v-for="item in overflowWorkspaceTabs"
                  :key="item.path"
                  :command="item.path"
                  :class="{ 'is-current': activeMenu === item.path }"
                >
                  <span class="workspace-tabs-overflow-item-mark" aria-hidden="true"></span>
                  <span class="workspace-tabs-overflow-label">
                    <span :title="item.title">{{ item.title }}</span>
                  </span>
                  <button
                    class="workspace-tabs-overflow-close"
                    type="button"
                    :aria-label="`关闭标签：${item.title}`"
                    title="关闭标签"
                    @click.stop="closeWorkspaceTab(item.path)"
                  >
                    ×
                  </button>
                </el-dropdown-item>
              </el-dropdown-menu>
            </div>
          </template>
        </el-dropdown>
      </div>

      <main class="desktop-content">
        <router-view v-slot="{ Component, route: currentRoute }">
          <transition name="desktop-route" mode="out-in">
            <KeepAlive :max="DESKTOP_WORKSPACE_TAB_CACHE_MAX">
              <component
                :is="Component"
                v-if="Component"
                :key="desktopRouteCacheKey(currentRoute)"
                class="desktop-route-shell"
              />
            </KeepAlive>
          </transition>
        </router-view>
      </main>
    </section>
  </div>

  <el-dialog
    v-model="profileDialogVisible"
    class="profile-settings-dialog"
    :show-close="false"
    :close-on-click-modal="false"
    :close-on-press-escape="false"
    :before-close="beforeProfileDialogClose"
    width="980px"
    align-center
    destroy-on-close
    @closed="profileDialogDirty = false"
  >
    <ProfileSettings
      @close-request="requestProfileDialogClose"
      @dirty-change="profileDialogDirty = $event"
      @saved="profileDialogVisible = false"
    />
  </el-dialog>

  <DesktopCommandPalette v-model="commandPaletteVisible" :commands="desktopCommands" />

  <el-dialog
    v-model="desktopPreferencesVisible"
    class="desktop-preferences-dialog"
    :show-close="false"
    :close-on-click-modal="true"
    width="940px"
    align-center
    destroy-on-close
  >
    <DesktopPreferences @close-request="desktopPreferencesVisible = false" />
  </el-dialog>

  <div
    v-if="workspaceTabMenu.visible && workspaceTabMenu.tab"
    class="workspace-tab-context-menu"
    :style="{ left: `${workspaceTabMenu.x}px`, top: `${workspaceTabMenu.y}px` }"
    @click.stop
  >
    <button type="button" @click="navigateWorkspaceTabFromMenu(workspaceTabMenu.tab.path)">切换到标签</button>
    <button type="button" @click="toggleWorkspaceTabFavorite(workspaceTabMenu.tab)">
      {{ isWorkspaceTabFavorited(workspaceTabMenu.tab) ? "取消收藏模块" : "收藏模块" }}
    </button>
    <button type="button" @click="closeWorkspaceTabFromMenu(workspaceTabMenu.tab.path)">关闭标签</button>
    <button type="button" :disabled="workspaceTabs.length <= 1" @click="closeOtherWorkspaceTabs(workspaceTabMenu.tab.path)">
      关闭其他标签
    </button>
    <button type="button" :disabled="!lastClosedWorkspaceTab" @click="restoreLastClosedWorkspaceTab">
      重新打开最近关闭
    </button>
  </div>
</template>

<script setup lang="ts">
import { computed, onBeforeUnmount, onMounted, ref, watch } from "vue";
import { useRoute, useRouter } from "vue-router";
import {
  ArrowDown,
  Bell,
  Box,
  Calendar,
  CircleCheck,
  Clock,
  Coin,
  DataAnalysis,
  Document,
  Download,
  Files,
  Flag,
  Folder,
  House,
  Key,
  Monitor,
  Notebook,
  Refresh,
  Search,
  Setting,
  Star,
  StarFilled,
  Suitcase,
  SwitchButton,
  User,
  UserFilled,
} from "@element-plus/icons-vue";
import { ElMessageBox } from "element-plus";
import { useAuthStore } from "../store/auth";
import { useStudyStore } from "../store/study";
import { fetchOverdueAesCount } from "../api/dashboard";
import { listMonitoringVisitIssues } from "../api/monitoringVisitIssues";
import { TEXT } from "../locales";
import { forceLogout, LOGOUT_REASON_MANUAL } from "../session/sessionManager";
import { DESKTOP_SESSION_RESTORE_PATH, shouldPreserveDesktopSessionOnAuthCheckFailure } from "../session/authRecovery";
import {
  checkDesktopUpdateAndPrompt,
  getDesktopUpdateStatus,
  listenDesktopUpdateStatus,
  type DesktopUpdateStatusSnapshot,
} from "../session/desktopUpdateManager";
import {
  clearFinishedDesktopActivities,
  getDesktopActivities,
  listenDesktopActivities,
  type DesktopActivityItem,
  type DesktopActivityStatus,
} from "../session/desktopActivityCenter";
import {
  DESKTOP_SERVER_URL_CHANGED_EVENT,
  DESKTOP_SHORTCUTS_CHANGED_EVENT,
  clearLegacyDesktopRouteHistoryPreference,
  formatDesktopShortcut,
  getAppMetadata,
  getDesktopServerUrl,
  listenDesktopMenuCommand,
  readDesktopFavoriteRoutes,
  readDesktopShortcutPreferences,
  toggleDesktopFavoriteRoute,
  type DesktopRoutePreference,
  type DesktopShortcutPreferences,
} from "../runtime";
import { dispatchDesktopRefreshCurrentView } from "../composables/useDesktopRefresh";
import { useDesktopShortcuts } from "../composables/useDesktopShortcuts";
import { getProjectRoutePermission, hasProjectPermission, projectRouteLandingPaths } from "../utils/projectRoutePermissions";
import type { DesktopCommand } from "../types/desktopCommands";
import DesktopCommandPalette from "./DesktopCommandPalette.vue";
import DesktopPreferences from "../views/DesktopPreferences.vue";
import ProfileSettings from "../views/ProfileSettings.vue";
import {
  buildAdminNavigationItems,
  buildProjectNavigationItems,
  flattenLayoutNavigationItems,
  getActiveLayoutPath,
  type LayoutNavigationIcon,
  type LayoutNavigationItem,
} from "./layout/navigation";
import {
  createWorkspaceTabHistory,
  currentWorkspaceTabPath,
  moveWorkspaceTabHistory,
  recordWorkspaceTabPath,
  replaceWorkspaceTabPath,
  type WorkspaceTabHistory,
} from "./layout/workspaceTabHistory";

const auth = useAuthStore();
const study = useStudyStore();
const router = useRouter();
const route = useRoute();
const desktopMetadata = getAppMetadata();
const DESKTOP_WORKSPACE_TAB_CACHE_MAX = 12;
const DESKTOP_WORKSPACE_TAB_MIN_WIDTH = 130;
const DESKTOP_WORKSPACE_TAB_GAP = 6;
const DESKTOP_WORKSPACE_TABS_HORIZONTAL_PADDING = 20;
const DESKTOP_WORKSPACE_TABS_OVERFLOW_WIDTH = 106;
const DESKTOP_ADMIN_SECTION_LABEL = "系统管理";
const isAdmin = computed(() => !!auth.user?.is_admin);
const projectRole = computed(() => study.currentStudyRole || (study.currentStudy as any)?.role_in_study || "");
const canAccessAdminPermissions = computed(() => isAdmin.value || projectRole.value === "PM");
const canAccessProjectPath = (path: string) =>
  hasProjectPermission(study.currentPermissions, projectRole.value, getProjectRoutePermission(path), isAdmin.value);
const hasAnyProjectModuleAccess = computed(() => projectRouteLandingPaths.some((path) => canAccessProjectPath(path)));
const isAdminContext = computed(() => route.path.startsWith("/admin"));
const showSidebarProjectChooser = computed(() => !study.currentStudy && !isAdminContext.value);
const loggingOut = ref(false);
const commandPaletteVisible = ref(false);
const desktopPreferencesVisible = ref(false);
const profileDialogVisible = ref(false);
const profileDialogDirty = ref(false);
const desktopServerUrl = ref(getDesktopServerUrl());
const desktopUpdateStatus = ref<DesktopUpdateStatusSnapshot>(getDesktopUpdateStatus());
const desktopActivities = ref<DesktopActivityItem[]>(getDesktopActivities());
const desktopFavoriteRoutes = ref<DesktopRoutePreference[]>(readDesktopFavoriteRoutes());
const desktopShortcutPreferences = ref<DesktopShortcutPreferences>(readDesktopShortcutPreferences());
const workspaceTabs = ref<DesktopRoutePreference[]>([]);
const workspaceTabHistories = ref<Record<string, WorkspaceTabHistory>>({});
const workspaceTabsElement = ref<HTMLElement | null>(null);
const workspaceTabsAvailableWidth = ref(0);
const workspaceTabsOverflowOpen = ref(false);
const draggingWorkspaceTabPath = ref("");
const lastClosedWorkspaceTab = ref<DesktopRoutePreference | null>(null);
const lastClosedWorkspaceTabHistory = ref<WorkspaceTabHistory | null>(null);
const workspaceTabMenu = ref<{
  visible: boolean;
  x: number;
  y: number;
  tab: DesktopRoutePreference | null;
}>({
  visible: false,
  x: 0,
  y: 0,
  tab: null,
});
const expandedNavigationGroups = ref<Set<string>>(new Set());
const collapsedNavigationGroups = ref<Set<string>>(new Set());
const headerRemindersLoading = ref(false);
const headerReminderStats = ref({ overdueAes: 0, overdueMonitoringIssues: 0 });
let desktopMenuUnlisten: (() => void) | undefined;
let desktopUpdateStatusUnlisten: (() => void) | undefined;
let desktopActivityUnlisten: (() => void) | undefined;
let workspaceTabsResizeObserver: ResizeObserver | undefined;

const iconComponentMap = {
  audit: Document,
  calendar: Calendar,
  check: CircleCheck,
  coin: Coin,
  dashboard: House,
  document: Files,
  flag: Flag,
  folder: Box,
  key: Key,
  monitor: DataAnalysis,
  notebook: Notebook,
  project: Suitcase,
  settings: Setting,
  subject: User,
  users: UserFilled,
};

const iconFor = (icon: LayoutNavigationIcon) => iconComponentMap[icon] || Folder;

const activeMenu = computed(() => getActiveLayoutPath(route.path));
const navigationGroupKey = (item: LayoutNavigationItem) => `${item.group}:${item.label}:${item.path}`;
const isNavigationGroupActive = (item: LayoutNavigationItem) =>
  Boolean(item.children?.some((child) => activeMenu.value === child.path));
const isNavigationGroupExpanded = (item: LayoutNavigationItem) => {
  const key = navigationGroupKey(item);
  return expandedNavigationGroups.value.has(key) || (isNavigationGroupActive(item) && !collapsedNavigationGroups.value.has(key));
};
const toggleNavigationGroup = (item: LayoutNavigationItem) => {
  const key = navigationGroupKey(item);
  const nextExpanded = new Set(expandedNavigationGroups.value);
  const nextCollapsed = new Set(collapsedNavigationGroups.value);
  if (isNavigationGroupExpanded(item)) {
    nextExpanded.delete(key);
    nextCollapsed.add(key);
  } else {
    nextExpanded.add(key);
    nextCollapsed.delete(key);
  }
  expandedNavigationGroups.value = nextExpanded;
  collapsedNavigationGroups.value = nextCollapsed;
};
const adminNavigationItems = computed(() =>
  study.currentStudy
    ? []
    : buildAdminNavigationItems({
        hasUser: Boolean(auth.user),
        isAdmin: isAdmin.value,
        canAccessAdminPermissions: canAccessAdminPermissions.value,
      }),
);
const projectNavigationItems = computed(() =>
  buildProjectNavigationItems({
    hasCurrentStudy: Boolean(study.currentStudy),
    hasAnyProjectModuleAccess: hasAnyProjectModuleAccess.value,
    canAccessProjectPath,
  }),
);
const adminDesktopNavigationItems = computed(() => flattenLayoutNavigationItems(adminNavigationItems.value));
const projectDesktopNavigationItems = computed(() => flattenLayoutNavigationItems(projectNavigationItems.value));
const desktopNavigationItems = computed(() =>
  [...adminDesktopNavigationItems.value, ...projectDesktopNavigationItems.value],
);
const workspaceTabFallbackPath = computed(() => {
  if (isAdminContext.value) return adminDesktopNavigationItems.value[0]?.path || "/admin/projects";
  return study.currentStudy ? "/project/overview" : "/desktop/project-entry";
});

const userDisplayName = computed(
  () => auth.user?.full_name || auth.user?.username || auth.user?.email || TEXT.common.labels.userFallback,
);
const userDisplayInitial = computed(() => {
  const source = auth.user?.full_name || auth.user?.username || auth.user?.email || TEXT.common.labels.userInitialFallback;
  return source.charAt(0).toUpperCase();
});
const desktopProjectSectionLabel = computed(() => study.currentStudy?.name || TEXT.menu.currentProject);
const projectEntryMenuLabel = computed(() => (study.currentStudy ? "返回项目选择" : "选择项目"));
const desktopConnectionLabel = computed(() => (desktopServerUrl.value ? "已连接" : "未配置"));
const desktopConnectionClass = computed(() => (desktopServerUrl.value ? "is-connected" : "is-warning"));
const desktopUpdateNoticeVisible = computed(
  () =>
    Boolean(desktopUpdateStatus.value.pendingUpdate) &&
    !desktopUpdateStatus.value.checking &&
    !desktopUpdateStatus.value.installing &&
    !["postponed", "suppressed"].includes(desktopUpdateStatus.value.lastStatus),
);
const desktopUpdateNoticeLabel = computed(() => {
  const pending = desktopUpdateStatus.value.pendingUpdate;
  return pending ? `新版本 ${pending.version}` : "";
});
const desktopActivityBadgeCount = computed(() =>
  desktopActivities.value.filter((item) => item.status === "running" || item.status === "failed").length
);
const desktopActivityBadgeValue = computed(() => (desktopActivityBadgeCount.value > 9 ? "9+" : desktopActivityBadgeCount.value));
const hasFinishedDesktopActivities = computed(() =>
  desktopActivities.value.some((item) => item.status === "completed" || item.status === "cancelled" || item.status === "failed")
);
const desktopActivityStatusLabel = (status: DesktopActivityStatus) => {
  switch (status) {
    case "running":
      return "进行中";
    case "completed":
      return "完成";
    case "failed":
      return "失败";
    case "cancelled":
      return "已取消";
    default:
      return "";
  }
};
const formatDesktopActivityTime = (value: string) => {
  const date = new Date(value);
  if (Number.isNaN(date.getTime())) return "";
  return date.toLocaleTimeString("zh-CN", { hour: "2-digit", minute: "2-digit" });
};

const currentDesktopRoutePreference = computed(() => {
  const item = desktopNavigationItems.value.find((navItem) => navItem.path === activeMenu.value);
  if (!item) return null;
  return { path: item.path, title: item.label, group: item.group };
});
const visibleWorkspaceTabCapacity = computed(() => {
  const tabCount = workspaceTabs.value.length;
  const availableWidth = workspaceTabsAvailableWidth.value;
  if (!tabCount || availableWidth <= 0) return tabCount;
  const maxWithoutOverflow = Math.max(
    1,
    Math.floor((availableWidth + DESKTOP_WORKSPACE_TAB_GAP) / (DESKTOP_WORKSPACE_TAB_MIN_WIDTH + DESKTOP_WORKSPACE_TAB_GAP)),
  );
  if (tabCount <= maxWithoutOverflow) return tabCount;
  return Math.max(
    1,
    Math.floor(
      (availableWidth - DESKTOP_WORKSPACE_TABS_OVERFLOW_WIDTH) /
        (DESKTOP_WORKSPACE_TAB_MIN_WIDTH + DESKTOP_WORKSPACE_TAB_GAP),
    ),
  );
});
const visibleWorkspaceTabs = computed(() => {
  const capacity = visibleWorkspaceTabCapacity.value;
  if (workspaceTabs.value.length <= capacity) return workspaceTabs.value;
  const firstTabs = workspaceTabs.value.slice(0, capacity);
  const activeTab = workspaceTabs.value.find((item) => item.path === activeMenu.value);
  if (!activeTab || firstTabs.some((item) => item.path === activeTab.path)) return firstTabs;
  return [...firstTabs.slice(0, -1), activeTab];
});
const overflowWorkspaceTabs = computed(() => {
  const visiblePaths = new Set(visibleWorkspaceTabs.value.map((item) => item.path));
  return workspaceTabs.value.filter((item) => !visiblePaths.has(item.path));
});
const currentRouteFavorited = computed(() => {
  const current = currentDesktopRoutePreference.value;
  return Boolean(current && desktopFavoriteRoutes.value.some((item) => item.path === current.path));
});
const desktopToolbarTitle = computed(() => {
  const current = currentDesktopRoutePreference.value;
  return current?.title || String(route.meta?.title || TEXT.common.appName);
});

const desktopToolbarSubtitle = computed(() => {
  const current = currentDesktopRoutePreference.value;
  if (!current) return TEXT.common.appName;
  const isAdminRoute = adminDesktopNavigationItems.value.some((item) => item.path === current.path);
  if (isAdminRoute) {
    const section = current.group && current.group !== TEXT.menu.admin ? current.group : "";
    return [DESKTOP_ADMIN_SECTION_LABEL, section].filter(Boolean).join(" / ");
  }
  const isProjectRoute = projectDesktopNavigationItems.value.some((item) => item.path === current.path);
  if (isProjectRoute) {
    const section = current.group && current.group !== TEXT.menu.currentProject ? current.group : "";
    const projectName = study.currentStudy?.name || TEXT.menu.currentProject;
    return [projectName, section].filter(Boolean).join(" / ");
  }
  return current.group || TEXT.common.appName;
});

const canReadRiskIssueAes = computed(() => !isAdminContext.value && canAccessProjectPath("/risk-issues/sae"));
const canReadMonitoringIssues = computed(() => !isAdminContext.value && canAccessProjectPath("/risk-issues/monitoring-visits"));
const hasAnyRiskReminderAccess = computed(() => canReadRiskIssueAes.value || canReadMonitoringIssues.value);
const toHeaderNumber = (value: unknown) => {
  const num = typeof value === "number" ? value : Number(value);
  return Number.isFinite(num) && num > 0 ? num : 0;
};
const headerReminderItems = computed(() => [
  canReadRiskIssueAes.value && headerReminderStats.value.overdueAes > 0 ? {
    key: "overdueAes",
    command: "overdueAes",
    title: TEXT.common.labels.overdueAes,
    description: TEXT.common.labels.overdueAesDesc,
    count: headerReminderStats.value.overdueAes,
    tone: "danger",
  } : null,
  canReadMonitoringIssues.value && headerReminderStats.value.overdueMonitoringIssues > 0 ? {
    key: "overdueMonitoringIssues",
    command: "overdueMonitoringIssues",
    title: TEXT.common.labels.overdueMonitoringIssues,
    description: TEXT.common.labels.overdueMonitoringIssuesDesc,
    count: headerReminderStats.value.overdueMonitoringIssues,
    tone: "warning",
  } : null,
].filter(Boolean) as Array<{
  key: string;
  command: string;
  title: string;
  description: string;
  count: number;
  tone: "danger" | "warning";
}>);
const headerReminderTotal = computed(() => headerReminderItems.value.reduce((sum, item) => sum + item.count, 0));
const headerReminderBadgeValue = computed(() => (headerReminderTotal.value > 99 ? "99+" : headerReminderTotal.value));

const refreshDesktopRoutePreferences = () => {
  const allowedPaths = new Set(desktopNavigationItems.value.map((item) => item.path));
  desktopFavoriteRoutes.value = readDesktopFavoriteRoutes().filter((item) => allowedPaths.has(item.path));
};

const addWorkspaceTab = (routePreference: Pick<DesktopRoutePreference, "path" | "title" | "group">) => {
  const item = { ...routePreference, updatedAt: new Date().toISOString() };
  const existingIndex = workspaceTabs.value.findIndex((tab) => tab.path === item.path);
  if (existingIndex >= 0) {
    const next = [...workspaceTabs.value];
    next[existingIndex] = { ...next[existingIndex], ...item };
    workspaceTabs.value = next;
    return;
  }
  workspaceTabs.value = [...workspaceTabs.value, item];
};

const setWorkspaceTabHistory = (tabPath: string, history: WorkspaceTabHistory) => {
  workspaceTabHistories.value = { ...workspaceTabHistories.value, [tabPath]: history };
};

const removeWorkspaceTabHistory = (tabPath: string) => {
  if (!workspaceTabHistories.value[tabPath]) return;
  const next = { ...workspaceTabHistories.value };
  delete next[tabPath];
  workspaceTabHistories.value = next;
};

const recordWorkspaceTabRoute = (tabPath: string, fullPath: string) => {
  setWorkspaceTabHistory(tabPath, recordWorkspaceTabPath(workspaceTabHistories.value[tabPath], fullPath));
};

const replaceWorkspaceTabRoute = (tabPath: string, fullPath: string) => {
  setWorkspaceTabHistory(tabPath, replaceWorkspaceTabPath(workspaceTabHistories.value[tabPath], fullPath));
};

const workspaceTabDestination = (tabPath: string) =>
  currentWorkspaceTabPath(workspaceTabHistories.value[tabPath], tabPath);

const desktopRouteCacheKey = (currentRoute: { fullPath: string }) => currentRoute.fullPath;

const navigateWorkspaceTab = (path: string) => {
  if (activeMenu.value === path) return;
  void router.push(workspaceTabDestination(path));
};

const navigateCurrentWorkspaceTabHistory = (offset: -1 | 1) => {
  const tabPath = activeMenu.value;
  const movement = moveWorkspaceTabHistory(workspaceTabHistories.value[tabPath], offset);
  if (!movement) return;
  setWorkspaceTabHistory(tabPath, movement.history);
  void router.push(movement.path);
};

const navigateOverflowWorkspaceTab = (path: string) => {
  navigateWorkspaceTab(path);
};

const updateWorkspaceTabsAvailableWidth = () => {
  const element = workspaceTabsElement.value;
  workspaceTabsAvailableWidth.value = element
    ? Math.max(0, element.clientWidth - DESKTOP_WORKSPACE_TABS_HORIZONTAL_PADDING)
    : 0;
};

const observeWorkspaceTabsElement = (element: HTMLElement | null) => {
  workspaceTabsResizeObserver?.disconnect();
  if (!element) {
    workspaceTabsAvailableWidth.value = 0;
    return;
  }
  updateWorkspaceTabsAvailableWidth();
  if (typeof ResizeObserver === "undefined") return;
  workspaceTabsResizeObserver ||= new ResizeObserver(updateWorkspaceTabsAvailableWidth);
  workspaceTabsResizeObserver.observe(element);
};

const closeWorkspaceTabMenu = () => {
  if (!workspaceTabMenu.value.visible) return;
  workspaceTabMenu.value = { visible: false, x: 0, y: 0, tab: null };
};

const openWorkspaceTabMenu = (tab: DesktopRoutePreference, event: MouseEvent) => {
  workspaceTabMenu.value = {
    visible: true,
    x: Math.min(event.clientX, window.innerWidth - 196),
    y: Math.min(event.clientY, window.innerHeight - 184),
    tab,
  };
};

const navigateWorkspaceTabFromMenu = (path: string) => {
  navigateWorkspaceTab(path);
  closeWorkspaceTabMenu();
};

const isWorkspaceTabFavorited = (tab: DesktopRoutePreference) =>
  desktopFavoriteRoutes.value.some((item) => item.path === tab.path);

const toggleWorkspaceTabFavorite = (tab: DesktopRoutePreference) => {
  desktopFavoriteRoutes.value = toggleDesktopFavoriteRoute({ path: tab.path, title: tab.title, group: tab.group });
  refreshDesktopRoutePreferences();
  closeWorkspaceTabMenu();
};

const startWorkspaceTabDrag = (path: string, event: DragEvent) => {
  draggingWorkspaceTabPath.value = path;
  event.dataTransfer?.setData("text/plain", path);
  if (event.dataTransfer) event.dataTransfer.effectAllowed = "move";
};

const allowWorkspaceTabDrop = (event: DragEvent) => {
  event.preventDefault();
  if (event.dataTransfer) event.dataTransfer.dropEffect = "move";
};

const dropWorkspaceTab = (targetPath: string, event: DragEvent) => {
  const sourcePath = draggingWorkspaceTabPath.value || event.dataTransfer?.getData("text/plain") || "";
  draggingWorkspaceTabPath.value = "";
  if (!sourcePath || sourcePath === targetPath) return;
  const sourceIndex = workspaceTabs.value.findIndex((tab) => tab.path === sourcePath);
  const targetIndex = workspaceTabs.value.findIndex((tab) => tab.path === targetPath);
  if (sourceIndex < 0 || targetIndex < 0) return;
  const next = [...workspaceTabs.value];
  const [moved] = next.splice(sourceIndex, 1);
  next.splice(targetIndex, 0, moved);
  workspaceTabs.value = next;
};

const closeWorkspaceTab = (path: string) => {
  const index = workspaceTabs.value.findIndex((tab) => tab.path === path);
  if (index >= 0) {
    lastClosedWorkspaceTab.value = workspaceTabs.value[index];
    lastClosedWorkspaceTabHistory.value = workspaceTabHistories.value[path] || createWorkspaceTabHistory(path);
  }
  workspaceTabs.value = workspaceTabs.value.filter((tab) => tab.path !== path);
  removeWorkspaceTabHistory(path);
  if (activeMenu.value !== path) return;
  const next =
    workspaceTabs.value[index] ||
    workspaceTabs.value[index - 1] ||
    desktopFavoriteRoutes.value.find((item) => item.path !== path);
  if (next) {
    router.push(workspaceTabDestination(next.path));
  } else {
    router.push(workspaceTabFallbackPath.value);
  }
};

const closeWorkspaceTabFromMenu = (path: string) => {
  closeWorkspaceTab(path);
  closeWorkspaceTabMenu();
};

const closeOtherWorkspaceTabs = (path: string) => {
  const target = workspaceTabs.value.find((tab) => tab.path === path);
  if (!target) return;
  workspaceTabs.value = [target];
  const targetHistory = workspaceTabHistories.value[path];
  workspaceTabHistories.value = targetHistory ? { [path]: targetHistory } : {};
  if (activeMenu.value !== path) void router.push(workspaceTabDestination(path));
  closeWorkspaceTabMenu();
};

const restoreLastClosedWorkspaceTab = () => {
  const tab = lastClosedWorkspaceTab.value;
  if (!tab) return;
  addWorkspaceTab(tab);
  if (lastClosedWorkspaceTabHistory.value) {
    setWorkspaceTabHistory(tab.path, lastClosedWorkspaceTabHistory.value);
  }
  void router.push(workspaceTabDestination(tab.path));
  lastClosedWorkspaceTab.value = null;
  lastClosedWorkspaceTabHistory.value = null;
  closeWorkspaceTabMenu();
};

const updateDesktopServerUrl = () => {
  desktopServerUrl.value = getDesktopServerUrl();
};

const updateDesktopShortcutPreferences = (event?: Event) => {
  desktopShortcutPreferences.value =
    (event as CustomEvent<DesktopShortcutPreferences> | undefined)?.detail || readDesktopShortcutPreferences();
};

const openCommandPalette = () => {
  commandPaletteVisible.value = true;
};

const openDesktopPreferences = () => {
  desktopPreferencesVisible.value = true;
};

const openDesktopProjectEntry = async () => {
  study.clearCurrentStudy();
  workspaceTabs.value = [];
  workspaceTabHistories.value = {};
  lastClosedWorkspaceTab.value = null;
  lastClosedWorkspaceTabHistory.value = null;
  await router.push("/desktop/project-entry");
};

const toggleCurrentFavorite = () => {
  const current = currentDesktopRoutePreference.value;
  if (!current) return;
  desktopFavoriteRoutes.value = toggleDesktopFavoriteRoute(current);
  refreshDesktopRoutePreferences();
};

const refreshCurrentDesktopView = () => {
  const handled = dispatchDesktopRefreshCurrentView();
  if (!handled) router.go(0);
};

const handleDesktopMenuCommand = (command: string) => {
  if (command === "ctms.desktop.commandPalette") {
    openCommandPalette();
    return;
  }
  if (command === "ctms.desktop.preferences") {
    openDesktopPreferences();
    return;
  }
  if (command === "ctms.desktop.serverSettings") {
    openDesktopPreferences();
    return;
  }
  if (command === "ctms.desktop.refresh") {
    refreshCurrentDesktopView();
    return;
  }
  if (command === "ctms.desktop.back") {
    navigateCurrentWorkspaceTabHistory(-1);
    return;
  }
  if (command === "ctms.desktop.forward") {
    navigateCurrentWorkspaceTabHistory(1);
  }
};

const desktopCommands = computed<DesktopCommand[]>(() => {
  const navigationCommands: DesktopCommand[] = desktopNavigationItems.value.map((item) => ({
    id: `route:${item.path}`,
    title: item.label,
    group: item.group,
    keywords: item.keywords,
    visible: true,
    run: () => {
      void router.push(item.path);
    },
  }));
  const workspaceTabCommands: DesktopCommand[] = workspaceTabs.value.map((item) => ({
    id: `workspace-tab:${item.path}`,
    title: `切换标签：${item.title}`,
    group: "标签页",
    keywords: [item.title, item.group, item.path].filter((value): value is string => Boolean(value)),
    visible: true,
    run: () => navigateWorkspaceTab(item.path),
  }));
  return [
    {
      id: "desktop:tab-back",
      title: "当前标签后退",
      group: "标签页",
      shortcut: formatDesktopShortcut(desktopShortcutPreferences.value.back),
      visible: true,
      run: () => navigateCurrentWorkspaceTabHistory(-1),
    },
    {
      id: "desktop:tab-forward",
      title: "当前标签前进",
      group: "标签页",
      shortcut: formatDesktopShortcut(desktopShortcutPreferences.value.forward),
      visible: true,
      run: () => navigateCurrentWorkspaceTabHistory(1),
    },
    {
      id: "desktop:refresh",
      title: "刷新当前视图",
      group: "桌面",
      shortcut: formatDesktopShortcut(desktopShortcutPreferences.value.refresh),
      visible: true,
      run: refreshCurrentDesktopView,
    },
    {
      id: "desktop:preferences",
      title: "打开系统偏好",
      group: "桌面",
      shortcut: "⌘,",
      visible: true,
      run: openDesktopPreferences,
    },
    {
      id: "desktop:server-settings",
      title: "连接设置",
      group: "桌面",
      visible: true,
      run: openDesktopPreferences,
    },
    {
      id: "desktop:update",
      title: "检查桌面更新",
      group: "桌面",
      visible: true,
      run: async () => {
        await checkDesktopUpdateAndPrompt({ notifyWhenCurrent: true });
      },
    },
    {
      id: "desktop:project-entry",
      title: projectEntryMenuLabel.value,
      group: "桌面",
      visible: true,
      run: openDesktopProjectEntry,
    },
    {
      id: "desktop:favorite-current",
      title: currentRouteFavorited.value ? "取消收藏当前模块" : "收藏当前模块",
      group: "桌面",
      visible: Boolean(currentDesktopRoutePreference.value),
      run: toggleCurrentFavorite,
    },
    {
      id: "desktop:restore-closed-tab",
      title: "重新打开最近关闭标签",
      group: "标签页",
      visible: Boolean(lastClosedWorkspaceTab.value),
      run: restoreLastClosedWorkspaceTab,
    },
    ...workspaceTabCommands,
    ...navigationCommands,
  ];
});

const loadHeaderReminders = async () => {
  const studyId = study.currentStudy?.id;
  if (!studyId || isAdminContext.value || !hasAnyRiskReminderAccess.value) {
    headerReminderStats.value = { overdueAes: 0, overdueMonitoringIssues: 0 };
    return;
  }
  headerRemindersLoading.value = true;
  try {
    const [aesRes, monitoringRes] = await Promise.allSettled([
      canReadRiskIssueAes.value ? fetchOverdueAesCount(studyId) : Promise.resolve(null),
      canReadMonitoringIssues.value ? listMonitoringVisitIssues(studyId, { overdue: true, limit: 500 }) : Promise.resolve(null),
    ]);
    if (study.currentStudy?.id !== studyId || isAdminContext.value) return;
    headerReminderStats.value = {
      overdueAes: aesRes.status === "fulfilled" && aesRes.value ? toHeaderNumber((aesRes.value.data as any)?.total) : 0,
      overdueMonitoringIssues: monitoringRes.status === "fulfilled" && monitoringRes.value && Array.isArray(monitoringRes.value.data)
        ? monitoringRes.value.data.length
        : 0,
    };
  } finally {
    if (study.currentStudy?.id === studyId) {
      headerRemindersLoading.value = false;
    }
  }
};

const handleReminderCommand = (cmd: string) => {
  if (cmd === "overdueAes" && canReadRiskIssueAes.value) {
    router.push("/risk-issues/sae");
    return;
  }
  if (cmd === "overdueMonitoringIssues" && canReadMonitoringIssues.value) {
    router.push("/risk-issues/monitoring-visits");
  }
};

const logoutImmediately = async () => {
  await auth.logout();
  study.clearCurrentStudy();
  router.replace("/login");
};

const onLogout = async () => {
  if (loggingOut.value) return;
  const confirmed = await ElMessageBox.confirm(
    "退出后需要重新登录才能继续访问项目数据。",
    "确认退出登录",
    {
      type: "warning",
      confirmButtonText: "退出登录",
      cancelButtonText: TEXT.common.actions.cancel,
      distinguishCancelAndClose: true,
    },
  ).catch(() => null);
  if (!confirmed) return;
  loggingOut.value = true;
  try {
    await forceLogout(LOGOUT_REASON_MANUAL);
  } finally {
    window.setTimeout(() => {
      loggingOut.value = false;
    }, 300);
  }
};

const onCommand = (cmd: string) => {
  if (cmd === "logout") {
    onLogout();
  } else if (cmd === "profile") {
    profileDialogVisible.value = true;
  } else if (cmd === "projectEntry") {
    openDesktopProjectEntry();
  } else if (cmd === "desktopPreferences") {
    openDesktopPreferences();
  }
};

const confirmDiscardProfileChanges = async () => {
  if (!profileDialogDirty.value) return true;
  const confirmed = await ElMessageBox.confirm(
    "当前修改尚未保存，关闭后将丢失这些改动。",
    "确认关闭个人中心",
    {
      type: "warning",
      confirmButtonText: "关闭",
      cancelButtonText: "继续编辑",
    },
  ).catch(() => false);
  return Boolean(confirmed);
};

const requestProfileDialogClose = async () => {
  if (await confirmDiscardProfileChanges()) {
    profileDialogVisible.value = false;
  }
};

const beforeProfileDialogClose = async (done: () => void) => {
  if (await confirmDiscardProfileChanges()) {
    done();
  }
};

watch(
  () => route.fullPath,
  (fullPath, previousFullPath) => {
    study.setViewContext(null);
    const current = currentDesktopRoutePreference.value;
    if (current) {
      addWorkspaceTab(current);
      const previousTabPath = previousFullPath ? getActiveLayoutPath(router.resolve(previousFullPath).path) : "";
      const replacesCurrentTabEntry =
        previousTabPath === current.path && Boolean((window.history.state as { replaced?: boolean } | null)?.replaced);
      if (replacesCurrentTabEntry) {
        replaceWorkspaceTabRoute(current.path, fullPath);
      } else {
        recordWorkspaceTabRoute(current.path, fullPath);
      }
      refreshDesktopRoutePreferences();
    }
  },
);

watch(
  () => [study.currentStudy?.id, route.path] as const,
  () => {
    refreshDesktopRoutePreferences();
    loadHeaderReminders();
  },
);

watch(
  () => desktopNavigationItems.value.map((item) => item.path).join("|"),
  () => refreshDesktopRoutePreferences(),
);

watch(workspaceTabsElement, (element) => observeWorkspaceTabsElement(element), { flush: "post" });

onMounted(async () => {
  window.addEventListener(DESKTOP_SERVER_URL_CHANGED_EVENT, updateDesktopServerUrl);
  window.addEventListener(DESKTOP_SHORTCUTS_CHANGED_EVENT, updateDesktopShortcutPreferences);
  window.addEventListener("resize", updateWorkspaceTabsAvailableWidth);
  clearLegacyDesktopRouteHistoryPreference();
  const current = currentDesktopRoutePreference.value;
  if (current) {
    addWorkspaceTab(current);
    recordWorkspaceTabRoute(current.path, route.fullPath);
  }
  refreshDesktopRoutePreferences();
  desktopMenuUnlisten = await listenDesktopMenuCommand(handleDesktopMenuCommand).catch(() => undefined);
  desktopUpdateStatusUnlisten = listenDesktopUpdateStatus((status) => {
    desktopUpdateStatus.value = status;
  });
  desktopActivityUnlisten = listenDesktopActivities((items) => {
    desktopActivities.value = items;
  });
  if (auth.token && !auth.user) {
    try {
      await auth.fetchMe({ disableNetworkRetry: true, suppressErrorMessage: true });
    } catch (error) {
      if (shouldPreserveDesktopSessionOnAuthCheckFailure(error)) {
        await router.replace({ path: DESKTOP_SESSION_RESTORE_PATH, query: { redirect: route.fullPath } });
        return;
      }
      await logoutImmediately();
      return;
    }
  }
  loadHeaderReminders();
});

onBeforeUnmount(() => {
  window.removeEventListener(DESKTOP_SERVER_URL_CHANGED_EVENT, updateDesktopServerUrl);
  window.removeEventListener(DESKTOP_SHORTCUTS_CHANGED_EVENT, updateDesktopShortcutPreferences);
  window.removeEventListener("resize", updateWorkspaceTabsAvailableWidth);
  workspaceTabsResizeObserver?.disconnect();
  desktopMenuUnlisten?.();
  desktopUpdateStatusUnlisten?.();
  desktopActivityUnlisten?.();
});

useDesktopShortcuts(
  () => true,
  {
    openCommandPalette,
    closeActiveLayer: () => {
      if (workspaceTabMenu.value.visible) {
        closeWorkspaceTabMenu();
        return true;
      }
      if (commandPaletteVisible.value) {
        commandPaletteVisible.value = false;
        return true;
      }
      if (desktopPreferencesVisible.value) {
        desktopPreferencesVisible.value = false;
        return true;
      }
      return false;
    },
    navigateBack: () => navigateCurrentWorkspaceTabHistory(-1),
    navigateForward: () => navigateCurrentWorkspaceTabHistory(1),
    refreshCurrentView: refreshCurrentDesktopView,
  },
);
</script>

<style scoped>
.desktop-workbench {
  display: grid;
  grid-template-columns: 248px minmax(0, 1fr);
  height: 100vh;
  height: 100dvh;
  min-height: 0;
  min-width: 1180px;
  overflow: hidden;
  background: #eef2f6;
  color: #142033;
}

.icon-button {
  appearance: none;
  border: 0;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.icon-button:active {
  transform: scale(0.96);
}

.desktop-sidebar {
  display: flex;
  height: 100%;
  min-height: 0;
  min-width: 0;
  overflow: hidden;
  flex-direction: column;
  background: #f8fafc;
  border-right: 1px solid #d7e0ea;
}

.sidebar-head {
  flex: 0 0 auto;
  padding: 44px 10px 10px;
  border-bottom: 1px solid #dfe7f0;
}

.sidebar-title-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: 10px;
}

.sidebar-head h1 {
  margin: 0;
  overflow: hidden;
  color: #101827;
  font-size: 16px;
  line-height: 1.25;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.sidebar-app-brand {
  display: inline-grid;
  min-width: 0;
  grid-template-columns: 30px minmax(0, 1fr);
  align-items: center;
  gap: 9px;
}

.sidebar-app-icon {
  width: 30px;
  height: 30px;
  border-radius: 8px;
  background: #ffffff;
  box-shadow: 0 8px 18px rgba(15, 23, 42, 0.14);
  object-fit: contain;
}

.icon-button {
  width: 32px;
  height: 32px;
  border: 1px solid transparent;
  border-radius: 8px;
  background: transparent;
  color: #5d6f85;
  transition: background-color 0.16s ease, border-color 0.16s ease, color 0.16s ease, transform 0.16s ease;
}

.icon-button:hover {
  border-color: #c8d4e1;
  background: #ffffff;
  color: #172233;
}

.study-switcher-trigger {
  appearance: none;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 8px;
  width: 100%;
  min-height: 32px;
  margin-top: 0;
  padding: 0 8px;
  border: 1px solid #d7e0ea;
  border-radius: 6px;
  background: #ffffff;
  color: #26384f;
  cursor: pointer;
  text-align: left;
}

.study-switcher-trigger.empty {
  display: inline-flex;
  justify-content: center;
  justify-self: start;
  width: 104px;
  color: #3f5d75;
  font-weight: 700;
}

.sidebar-scroll {
  flex: 1;
  min-height: 0;
  max-height: 100%;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 10px 8px 24px;
  scrollbar-gutter: stable;
}

.sidebar-scroll::-webkit-scrollbar {
  width: 10px;
}

.sidebar-scroll::-webkit-scrollbar-track {
  background: transparent;
}

.sidebar-scroll::-webkit-scrollbar-thumb {
  border: 3px solid #f8fafc;
  border-radius: 999px;
  background: #c5d1df;
}

.sidebar-scroll::-webkit-scrollbar-thumb:hover {
  background: #aebdcd;
}

.nav-section + .nav-section {
  margin-top: 12px;
}

.section-label {
  padding: 0 6px 6px;
  color: #7b8da3;
  font-size: 11px;
  font-weight: 800;
}

.sidebar-link {
  appearance: none;
  display: grid;
  grid-template-columns: 16px minmax(0, 1fr);
  align-items: center;
  gap: 8px;
  width: 100%;
  min-height: 32px;
  margin: 1px 0;
  padding: 0 8px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: #3d5068;
  cursor: pointer;
  font: inherit;
  font-size: 14px;
  line-height: 1.3;
  text-align: left;
}

.sidebar-link.compact {
  grid-template-columns: 16px minmax(0, 1fr);
}

.sidebar-link.child {
  grid-template-columns: minmax(0, 1fr);
  min-height: 28px;
  padding-left: 32px;
  color: #607289;
  font-size: 12px;
}

.sidebar-link :deep(.el-icon) {
  font-size: 16px;
}

.sidebar-link span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.sidebar-link:hover {
  background: #edf3f8;
  color: #142033;
}

.sidebar-link.active {
  background: #e4edf6;
  color: #183756;
  font-weight: 760;
}

.sidebar-group {
  margin: 1px 0 4px;
}

.group-title {
  grid-template-columns: 16px minmax(0, 1fr) 14px;
  font-weight: 700;
}

.group-expander {
  justify-self: end;
  color: #7a8ca2;
  font-size: 12px;
  transition: transform 0.16s ease;
}

.sidebar-group.expanded .group-expander {
  transform: rotate(180deg);
}

.sidebar-children {
  padding: 2px 0 4px;
}

.desktop-main {
  display: grid;
  grid-template-rows: 52px auto minmax(0, 1fr);
  min-width: 0;
  min-height: 0;
}

.desktop-toolbar {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 14px;
  padding: 0 12px;
  border-bottom: 1px solid #d7e0ea;
  background: rgba(248, 250, 252, 0.92);
  backdrop-filter: blur(14px);
}

.toolbar-left,
.toolbar-right {
  display: flex;
  align-items: center;
  min-width: 0;
}

.toolbar-left {
  align-self: stretch;
  cursor: default;
  user-select: none;
}

.toolbar-right {
  justify-content: flex-end;
  gap: 8px;
}

.toolbar-right,
.toolbar-right :deep(*),
.desktop-sidebar button,
.desktop-sidebar :deep(.el-dropdown),
.workspace-tabs,
.workspace-tabs *,
.workspace-tab-context-menu,
.workspace-tab-context-menu * {
  -webkit-app-region: no-drag;
}

.toolbar-left[data-tauri-drag-region],
.toolbar-context[data-tauri-drag-region],
.toolbar-title[data-tauri-drag-region],
.toolbar-subtitle[data-tauri-drag-region] {
  -webkit-app-region: drag;
}

.toolbar-context {
  display: grid;
  min-width: 0;
  align-content: center;
  gap: 2px;
  width: 100%;
  height: 100%;
  overflow: hidden;
}

.toolbar-title,
.toolbar-subtitle {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.toolbar-title {
  color: #142033;
  font-size: 14px;
  font-weight: 800;
  line-height: 1.25;
}

.toolbar-subtitle {
  color: #66778d;
  font-size: 11px;
  font-weight: 700;
  line-height: 1.2;
}

.command-trigger,
.update-notice,
.connection-button,
.account-button {
  appearance: none;
  display: inline-flex;
  align-items: center;
  height: 32px;
  border: 1px solid #d2dce8;
  border-radius: 8px;
  background: #ffffff;
  color: #33475f;
  cursor: pointer;
  font: inherit;
  font-size: 12px;
  font-weight: 700;
}

.update-notice {
  gap: 6px;
  max-width: 138px;
  padding: 0 9px;
  border-color: #d8bd6c;
  background: #fff8e5;
  color: #6f5317;
}

.update-notice span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.command-trigger {
  gap: 7px;
  padding: 0 8px;
}

.command-trigger kbd {
  padding: 1px 5px;
  border: 1px solid #c7d2df;
  border-radius: 5px;
  background: #f6f8fb;
  color: #66778d;
  font-size: 10px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
}

.connection-button {
  gap: 7px;
  padding: 0 9px;
}

.connection-dot {
  width: 7px;
  height: 7px;
  border-radius: 999px;
  background: #c58b2a;
}

.connection-dot.is-connected {
  background: #3f8f6b;
}

.connection-panel {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.panel-title {
  color: #101827;
  font-size: 14px;
  font-weight: 800;
}

.panel-subtitle {
  color: #8a9bb0;
  font-size: 12px;
}

.panel-row {
  display: grid;
  grid-template-columns: 56px minmax(0, 1fr);
  gap: 10px;
  color: #66778d;
  font-size: 12px;
}

.panel-row code {
  overflow-wrap: anywhere;
  color: #101827;
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
}

.reminder-button {
  position: relative;
}

.reminder-badge {
  display: inline-flex;
}

.reminder-badge :deep(.el-badge__content) {
  height: 16px;
  min-width: 16px;
  padding: 0 4px;
  border: 2px solid #ffffff;
  background: #c24b4b;
  font-size: 10px;
  line-height: 12px;
}

.reminder-menu {
  padding: 0 !important;
}

.reminder-panel {
  width: 314px;
  padding: 10px;
}

.reminder-head {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 4px 4px 10px;
}

.reminder-total {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 22px;
  height: 22px;
  padding: 0 7px;
  border-radius: 999px;
  background: #fee2e2;
  color: #b91c1c;
  font-size: 12px;
  font-weight: 800;
}

.reminder-item {
  width: 100%;
  height: auto;
  padding: 8px 7px !important;
  border-radius: 7px;
}

.reminder-mark {
  width: 7px;
  height: 28px;
  border-radius: 999px;
}

.reminder-mark.is-danger {
  background: #c24b4b;
}

.reminder-mark.is-warning {
  background: #c58b2a;
}

.reminder-body {
  display: inline-flex;
  flex: 1;
  min-width: 0;
  flex-direction: column;
  gap: 2px;
  margin: 0 10px;
}

.reminder-body span,
.reminder-body small {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.reminder-body span {
  color: #142033;
  font-size: 13px;
  font-weight: 700;
}

.reminder-body small {
  color: #7b8da3;
  font-size: 12px;
}

.reminder-empty {
  padding: 20px 8px;
  color: #8a9bb0;
  font-size: 13px;
  text-align: center;
}

.activity-button {
  position: relative;
}

.activity-badge {
  display: inline-flex;
}

.activity-badge :deep(.el-badge__content) {
  height: 16px;
  min-width: 16px;
  padding: 0 4px;
  border: 2px solid #ffffff;
  background: #3f5d75;
  font-size: 10px;
  line-height: 12px;
}

.activity-panel {
  width: 320px;
  padding: 10px;
}

.activity-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  padding: 4px 4px 10px;
}

.activity-clear {
  appearance: none;
  height: 24px;
  padding: 0 7px;
  border: 1px solid #d2dce8;
  border-radius: 6px;
  background: #ffffff;
  color: #52667e;
  cursor: pointer;
  font: inherit;
  font-size: 11px;
  font-weight: 700;
}

.activity-clear:hover {
  background: #eef4fb;
  color: #183756;
}

.activity-list {
  display: flex;
  max-height: 286px;
  flex-direction: column;
  gap: 3px;
  overflow-y: auto;
}

.activity-item {
  display: grid;
  grid-template-columns: 7px minmax(0, 1fr) auto;
  align-items: center;
  gap: 9px;
  min-height: 46px;
  padding: 7px;
  border-radius: 7px;
}

.activity-item:hover {
  background: #f4f7fa;
}

.activity-mark {
  width: 7px;
  height: 28px;
  border-radius: 999px;
  background: #7b8da3;
}

.activity-mark.is-running {
  background: #3f5d75;
}

.activity-mark.is-completed {
  background: #3f8f6b;
}

.activity-mark.is-failed {
  background: #c24b4b;
}

.activity-mark.is-cancelled {
  background: #9aaabd;
}

.activity-body {
  display: inline-flex;
  min-width: 0;
  flex-direction: column;
  gap: 2px;
}

.activity-title,
.activity-detail {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.activity-title {
  color: #142033;
  font-size: 13px;
  font-weight: 800;
}

.activity-detail {
  color: #7b8da3;
  font-size: 12px;
  font-weight: 600;
}

.activity-meta {
  display: inline-flex;
  min-width: 44px;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
  color: #52667e;
  font-size: 12px;
  font-weight: 800;
}

.activity-meta small {
  color: #9aaabd;
  font-size: 10px;
  font-weight: 700;
}

.activity-empty {
  padding: 24px 8px;
  color: #8a9bb0;
  font-size: 13px;
  text-align: center;
}

.account-button {
  gap: 7px;
  max-width: 188px;
  padding: 0 8px 0 3px;
}

.account-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: 7px;
  background: #3f5d75;
  color: #ffffff;
  font-size: 12px;
  font-weight: 800;
}

.account-name {
  max-width: 110px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.workspace-tabs {
  display: flex;
  align-items: center;
  min-width: 0;
  gap: 6px;
  padding: 7px 10px;
  overflow: hidden;
  border-bottom: 1px solid #dfe7f0;
  background: #f4f7fa;
}

.workspace-tabs-list {
  display: grid;
  flex: 1 1 auto;
  min-width: 0;
  gap: 6px;
  overflow: hidden;
}

.workspace-tab {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 8px;
  min-width: 0;
  height: 30px;
  box-sizing: border-box;
  padding: 0 5px 0 10px;
  border: 1px solid #d2dce8;
  border-radius: 8px;
  background: #ffffff;
  color: #3d5068;
  cursor: grab;
  font-size: 12px;
  font-weight: 700;
}

.workspace-tab:active {
  cursor: grabbing;
}

.workspace-tab.active {
  border-color: #aebfd1;
  background: #eaf1f8;
  color: #183756;
}

.workspace-tab.dragging {
  opacity: 0.58;
}

.workspace-tab-action {
  appearance: none;
  display: block;
  min-width: 0;
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  cursor: pointer;
  font: inherit;
  font-weight: inherit;
}

.workspace-tab-action span {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.tab-close {
  width: 18px;
  height: 18px;
  padding: 0;
  border: 0;
  border-radius: 5px;
  background: transparent;
  color: #7b8da3;
  cursor: pointer;
}

.tab-close:hover {
  background: rgba(15, 23, 42, 0.08);
  color: #142033;
}

.workspace-tabs-overflow {
  display: flex;
  flex: 0 0 106px;
  min-width: 0;
}

.workspace-tabs-overflow-trigger {
  appearance: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 106px;
  height: 30px;
  gap: 6px;
  padding: 0 9px 0 11px;
  border: 1px solid #c5d1df;
  border-radius: 8px;
  background: #ffffff;
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
  color: #3d5068;
  cursor: pointer;
  font: inherit;
  font-size: 12px;
  font-weight: 700;
  white-space: nowrap;
}

.workspace-tabs-overflow-trigger:hover,
.workspace-tabs-overflow-trigger:focus-visible,
.workspace-tabs-overflow-trigger.is-open {
  border-color: #aebfd1;
  background: #eaf1f8;
  color: #183756;
  outline: none;
}

.workspace-tabs-overflow-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 18px;
  box-sizing: border-box;
  padding: 0 6px;
  border-radius: 9px;
  background: #e3ebf4;
  color: #31506f;
  font-size: 10px;
  line-height: 1;
}

:global(.workspace-tabs-overflow-popper.el-popper) {
  overflow: hidden;
  padding: 0;
  border: 1px solid #d6e0eb;
  border-radius: 10px;
  background: #ffffff;
  box-shadow: 0 14px 38px rgba(15, 23, 42, 0.16), 0 2px 8px rgba(15, 23, 42, 0.08);
}

:global(.workspace-tabs-overflow-popper .el-popper__arrow::before) {
  border-color: #d6e0eb;
  background: #ffffff;
}

.workspace-tabs-overflow-menu {
  width: 252px;
}

.workspace-tabs-overflow-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 38px;
  padding: 0 12px;
  border-bottom: 1px solid #e5ebf2;
  color: #2d4058;
  font-size: 12px;
  font-weight: 800;
}

.workspace-tabs-overflow-header span:last-child {
  color: #8292a6;
  font-size: 11px;
  font-weight: 700;
}

:global(.workspace-tabs-overflow-popper .el-dropdown-menu) {
  min-width: 0;
  max-height: min(360px, calc(100vh - 120px));
  padding: 6px;
  border: 0;
  background: transparent;
  overflow-y: auto;
}

:global(.workspace-tabs-overflow-popper .el-dropdown-menu__item) {
  display: flex;
  min-height: 38px;
  gap: 9px;
  padding: 0 6px 0 10px;
  border-radius: 7px;
  color: #33485f;
  font-size: 12px;
  font-weight: 700;
}

:global(.workspace-tabs-overflow-popper .el-dropdown-menu__item:hover),
:global(.workspace-tabs-overflow-popper .el-dropdown-menu__item:focus) {
  background: #f0f5fa;
  color: #183756;
}

:global(.workspace-tabs-overflow-popper .el-dropdown-menu__item.is-current) {
  background: #eaf1f8;
  color: #183756;
}

.workspace-tabs-overflow-label {
  display: block;
  flex: 1 1 auto;
  min-width: 0;
}

.workspace-tabs-overflow-item-mark {
  flex: 0 0 7px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #9fb1c4;
}

.workspace-tabs-overflow-label > span {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.workspace-tabs-overflow-close {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 24px;
  width: 24px;
  height: 24px;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: #7b8da3;
  cursor: pointer;
  font: inherit;
  font-size: 16px;
  opacity: 0.62;
}

.workspace-tabs-overflow-close:hover,
.workspace-tabs-overflow-close:focus-visible {
  background: rgba(15, 23, 42, 0.08);
  color: #142033;
  opacity: 1;
  outline: none;
}

.workspace-tab-context-menu {
  position: fixed;
  z-index: 2300;
  display: flex;
  width: 184px;
  flex-direction: column;
  gap: 2px;
  padding: 6px;
  border: 1px solid #cbd7e5;
  border-radius: 8px;
  background: #ffffff;
  box-shadow: 0 14px 38px rgba(15, 23, 42, 0.16);
}

.workspace-tab-context-menu button {
  appearance: none;
  display: flex;
  align-items: center;
  width: 100%;
  min-height: 30px;
  padding: 0 9px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: #223349;
  cursor: pointer;
  font: inherit;
  font-size: 12px;
  font-weight: 700;
  text-align: left;
}

.workspace-tab-context-menu button:hover:not(:disabled) {
  background: #eef4fb;
  color: #183756;
}

.workspace-tab-context-menu button:disabled {
  cursor: not-allowed;
  color: #9aaabd;
}

.desktop-content {
  min-width: 0;
  min-height: 0;
  overflow: auto;
  padding: 0;
  background: #eef2f6;
}

.desktop-route-shell {
  height: 100%;
  min-height: 100%;
}

.desktop-route-enter-active,
.desktop-route-leave-active {
  transition: opacity 140ms ease, transform 160ms ease;
}

.desktop-route-enter-from {
  opacity: 0;
  transform: translateY(4px);
}

.desktop-route-leave-to {
  opacity: 0;
  transform: translateY(-3px);
}

/* profile-settings-dialog：移除外层卡片外壳，只保留容器行为 */
:global(.profile-settings-dialog) {
  --el-dialog-bg-color: transparent !important;
  --el-dialog-box-shadow: none !important;
  --el-dialog-border-radius: 0 !important;
  overflow: visible !important;
  background: transparent !important;
  box-shadow: none !important;
  border-radius: 0 !important;
  border: none !important;
}

:global(.desktop-preferences-dialog) {
  /* 移除外层卡片外壳，内容区自带圆角阴影 */
  --el-dialog-bg-color: transparent !important;
  --el-dialog-box-shadow: none !important;
  --el-dialog-border-radius: 0 !important;
  overflow: visible !important;
  background: transparent !important;
  box-shadow: none !important;
  border-radius: 0 !important;
  border: none !important;
}

:global(.profile-settings-dialog .el-dialog__header),
:global(.desktop-preferences-dialog .el-dialog__header) {
  display: none;
}

:global(.profile-settings-dialog .el-dialog__body) {
  padding: 0;
  background: transparent;
}

:global(.desktop-preferences-dialog .el-dialog__body) {
  padding: 0;
  background: transparent;
}

:global([data-ctms-theme="dark"] .desktop-workbench) {
  background: #0f172a;
  color: #e5edf7;
}

:global([data-ctms-theme="dark"] .desktop-sidebar) {
  border-right-color: #26364a;
  background: #111827;
}

:global([data-ctms-theme="dark"] .sidebar-head) {
  border-bottom-color: #26364a;
}

:global([data-ctms-theme="dark"] .section-label),
:global([data-ctms-theme="dark"] .panel-subtitle),
:global([data-ctms-theme="dark"] .toolbar-subtitle) {
  color: #94a3b8;
}

:global([data-ctms-theme="dark"] .sidebar-head h1),
:global([data-ctms-theme="dark"] .toolbar-title),
:global([data-ctms-theme="dark"] .panel-title),
:global([data-ctms-theme="dark"] .panel-row code),
:global([data-ctms-theme="dark"] .reminder-body span) {
  color: #f8fafc;
}

:global([data-ctms-theme="dark"] .icon-button) {
  color: #9aaabd;
}

:global([data-ctms-theme="dark"] .icon-button:hover) {
  border-color: #334155;
  background: #1f2d3d;
  color: #f8fafc;
}

:global([data-ctms-theme="dark"] .study-switcher-trigger),
:global([data-ctms-theme="dark"] .command-trigger),
:global([data-ctms-theme="dark"] .update-notice),
:global([data-ctms-theme="dark"] .connection-button),
:global([data-ctms-theme="dark"] .account-button),
:global([data-ctms-theme="dark"] .workspace-tab),
:global([data-ctms-theme="dark"] .workspace-tabs-overflow-trigger) {
  border-color: #334155;
  background: #172033;
  color: #dbe5f1;
}

:global([data-ctms-theme="dark"] .study-switcher-trigger.empty) {
  color: #bfdbfe;
}

:global([data-ctms-theme="dark"] .sidebar-link) {
  color: #cbd5e1;
}

:global([data-ctms-theme="dark"] .sidebar-link.child) {
  color: #9aaabd;
}

:global([data-ctms-theme="dark"] .sidebar-link:hover) {
  background: #1f2d3d;
  color: #f8fafc;
}

:global([data-ctms-theme="dark"] .sidebar-link.active),
:global([data-ctms-theme="dark"] .workspace-tab.active) {
  border-color: #3e5c77;
  background: #243247;
  color: #bfdbfe;
}

:global([data-ctms-theme="dark"] .desktop-toolbar) {
  border-bottom-color: #26364a;
  background: rgba(17, 24, 39, 0.92);
}

:global([data-ctms-theme="dark"] .workspace-tabs) {
  border-bottom-color: #26364a;
  background: #111827;
}

:global([data-ctms-theme="dark"] .workspace-tabs-overflow-count) {
  background: #243247;
  color: #bfdbfe;
}

:global([data-ctms-theme="dark"] .workspace-tabs-overflow-popper .el-dropdown-menu) {
  background: transparent;
}

:global([data-ctms-theme="dark"] .workspace-tabs-overflow-popper.el-popper) {
  border-color: #334155;
  background: #172033;
}

:global([data-ctms-theme="dark"] .workspace-tabs-overflow-popper .el-popper__arrow::before) {
  border-color: #334155;
  background: #172033;
}

:global([data-ctms-theme="dark"] .workspace-tabs-overflow-header) {
  border-bottom-color: #334155;
  color: #e5edf7;
}

:global([data-ctms-theme="dark"] .workspace-tabs-overflow-header span:last-child) {
  color: #94a3b8;
}

:global([data-ctms-theme="dark"] .workspace-tabs-overflow-popper .el-dropdown-menu__item) {
  color: #dbe5f1;
}

:global([data-ctms-theme="dark"] .workspace-tabs-overflow-popper .el-dropdown-menu__item:hover),
:global([data-ctms-theme="dark"] .workspace-tabs-overflow-popper .el-dropdown-menu__item:focus),
:global([data-ctms-theme="dark"] .workspace-tabs-overflow-popper .el-dropdown-menu__item.is-current) {
  background: #243247;
  color: #bfdbfe;
}

:global([data-ctms-theme="dark"] .workspace-tabs-overflow-item-mark) {
  background: #6f87a1;
}

:global([data-ctms-theme="dark"] .workspace-tabs-overflow-close) {
  color: #94a3b8;
}

:global([data-ctms-theme="dark"] .workspace-tabs-overflow-close:hover),
:global([data-ctms-theme="dark"] .workspace-tabs-overflow-close:focus-visible) {
  background: rgba(226, 232, 240, 0.1);
  color: #f8fafc;
}

:global([data-ctms-theme="dark"] .desktop-content) {
  background: #0f172a;
}

:global([data-ctms-theme="dark"] .command-trigger kbd) {
  border-color: #334155;
  background: #0f172a;
  color: #94a3b8;
}

:global([data-ctms-theme="dark"] .account-avatar) {
  background: #3e5c77;
}

:global([data-ctms-theme="dark"] .workspace-tab-context-menu) {
  border-color: #334155;
  background: #172033;
}

:global([data-ctms-theme="dark"] .workspace-tab-context-menu button) {
  color: #dbe5f1;
}

:global([data-ctms-theme="dark"] .workspace-tab-context-menu button:hover:not(:disabled)) {
  background: #243247;
  color: #bfdbfe;
}

:global([data-ctms-theme="dark"] .workspace-tab-context-menu button:disabled) {
  color: #64748b;
}

:global([data-ctms-theme="dark"] .tab-close) {
  color: #94a3b8;
}

:global([data-ctms-theme="dark"] .tab-close:hover) {
  background: rgba(226, 232, 240, 0.1);
  color: #f8fafc;
}

:global([data-ctms-theme="dark"] .connection-panel) {
  color: #dbe5f1;
}

:global([data-ctms-theme="dark"] .desktop-activity-popover) {
  border-color: #334155;
  background: #172033;
}

:global([data-ctms-theme="dark"] .desktop-activity-popover .activity-item:hover) {
  background: #1f2d3d;
}

:global([data-ctms-theme="dark"] .desktop-activity-popover .activity-title) {
  color: #f8fafc;
}

:global([data-ctms-theme="dark"] .desktop-activity-popover .activity-detail),
:global([data-ctms-theme="dark"] .desktop-activity-popover .activity-meta small),
:global([data-ctms-theme="dark"] .desktop-activity-popover .activity-empty) {
  color: #94a3b8;
}

:global([data-ctms-theme="dark"] .desktop-activity-popover .activity-meta) {
  color: #cbd5e1;
}

:global([data-ctms-theme="dark"] .desktop-activity-popover .activity-clear) {
  border-color: #334155;
  background: #172033;
  color: #cbd5e1;
}

:global([data-ctms-theme="dark"] .desktop-activity-popover .activity-clear:hover) {
  background: #243247;
  color: #bfdbfe;
}

:global([data-ctms-theme="dark"] .panel-row) {
  color: #94a3b8;
}

:global([data-ctms-theme="dark"] .reminder-total) {
  background: rgba(242, 139, 139, 0.14);
  color: #fecaca;
}

:global([data-ctms-theme="dark"] .reminder-body small),
:global([data-ctms-theme="dark"] .reminder-empty) {
  color: #94a3b8;
}

:global([data-ctms-theme="dark"] .sidebar-scroll::-webkit-scrollbar-thumb) {
  border-color: #111827;
  background: #334155;
}

:global([data-ctms-theme="dark"] .sidebar-scroll::-webkit-scrollbar-thumb:hover) {
  background: #475569;
}

:global([data-ctms-theme="dark"] .profile-settings-dialog),
:global([data-ctms-theme="dark"] .desktop-preferences-dialog) {
  background: #111827;
}

:global([data-ctms-theme="dark"] .desktop-preferences-dialog .el-dialog__body) {
  /* 与深色侧边栏 --pref-bg-sidebar 保持一致 */
  background: #131b28;
}

@media (prefers-reduced-motion: reduce) {
  .icon-button,
  .desktop-route-enter-active,
  .desktop-route-leave-active {
    transition-duration: 1ms !important;
  }

  .desktop-route-enter-from,
  .desktop-route-leave-to {
    transform: none;
  }
}
</style>
