This commit is contained in:
Baobhan Sith
2025-04-30 09:58:08 +08:00
parent 8845392eee
commit 95a0a52185
17 changed files with 83 additions and 133 deletions
@@ -3,8 +3,8 @@ import { ref, reactive, watch, computed, onMounted } from 'vue'; // 添加 onMou
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
import { useProxiesStore, ProxyInfo } from '../stores/proxies.store';
import { useTagsStore } from '../stores/tags.store'; // 引入标签 Store
import TagInput from './TagInput.vue'; // 导入新的 TagInput 组件
import { useTagsStore } from '../stores/tags.store';
import TagInput from './TagInput.vue';
// 定义组件发出的事件
const emit = defineEmits(['close', 'proxy-added', 'proxy-updated']);
@@ -1,23 +1,18 @@
<script setup lang="ts">
import { ref, watch, nextTick, onMounted, onBeforeUnmount, defineExpose, computed } from 'vue'; // Import computed
import { ref, watch, nextTick, onMounted, onBeforeUnmount, defineExpose, computed } from 'vue';
import { useI18n } from 'vue-i18n';
import { storeToRefs } from 'pinia'; // Import storeToRefs
import { useFocusSwitcherStore } from '../stores/focusSwitcher.store'; // 导入 Store
import { useSettingsStore } from '../stores/settings.store'; // NEW: Import settings store
import { useQuickCommandsStore } from '../stores/quickCommands.store'; // NEW: Import quick commands store
import { useCommandHistoryStore } from '../stores/commandHistory.store'; // NEW: Import command history store
// 假设你有一个图标库,例如 unplugin-icons 或类似库
// import SearchIcon from '~icons/mdi/magnify';
// import ArrowUpIcon from '~icons/mdi/arrow-up';
// import ArrowDownIcon from '~icons/mdi/arrow-down';
// import CloseIcon from '~icons/mdi/close';
import { storeToRefs } from 'pinia';
import { useFocusSwitcherStore } from '../stores/focusSwitcher.store';
import { useSettingsStore } from '../stores/settings.store';
import { useQuickCommandsStore } from '../stores/quickCommands.store';
import { useCommandHistoryStore } from '../stores/commandHistory.store';
const emit = defineEmits(['send-command', 'search', 'find-next', 'find-previous', 'close-search']); // 移除 open-focus-switcher-config 事件
const emit = defineEmits(['send-command', 'search', 'find-next', 'find-previous', 'close-search']);
const { t } = useI18n();
const focusSwitcherStore = useFocusSwitcherStore(); // +++ 实例化 Store +++
const settingsStore = useSettingsStore(); // NEW: Instantiate settings store
const quickCommandsStore = useQuickCommandsStore(); // NEW: Instantiate quick commands store
const commandHistoryStore = useCommandHistoryStore(); // NEW: Instantiate command history store
const focusSwitcherStore = useFocusSwitcherStore();
const settingsStore = useSettingsStore();
const quickCommandsStore = useQuickCommandsStore();
const commandHistoryStore = useCommandHistoryStore();
// Get reactive setting from store
const { commandInputSyncTarget } = storeToRefs(settingsStore);
@@ -1,18 +1,18 @@
<script setup lang="ts">
import { onMounted, computed, ref, reactive, watch } from 'vue'; // 统一导入, 添加 watch
import { onMounted, computed, ref, reactive, watch } from 'vue';
import { storeToRefs } from 'pinia';
import { useRouter } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { useConnectionsStore, ConnectionInfo } from '../stores/connections.store'; // 引入 ConnectionInfo 类型
import { useTagsStore } from '../stores/tags.store'; // 引入 Tags Store
import { useConnectionsStore, ConnectionInfo } from '../stores/connections.store';
import { useTagsStore } from '../stores/tags.store';
const { t } = useI18n(); // 获取 t 函数
const router = useRouter(); // 获取 router 实例
const tagsStore = useTagsStore(); // 获取 Tags Store 实例
// 使用 storeToRefs 来保持 state 属性的响应性
// 不再直接从 connectionsStore 获取 connections, isLoading, error
// const { connections, isLoading, error } = storeToRefs(connectionsStore);
const { tags: allTags, isLoading: isTagsLoading, error: tagsError } = storeToRefs(tagsStore); // 获取所有标签及其状态
const { t } = useI18n();
const router = useRouter();
const tagsStore = useTagsStore();
const { tags: allTags, isLoading: isTagsLoading, error: tagsError } = storeToRefs(tagsStore);
// 定义 Props,接收筛选后的连接列表
const props = defineProps<{
@@ -1,10 +1,10 @@
<script setup lang="ts">
import { computed } from 'vue';
import { useI18n } from 'vue-i18n';
import { useSessionStore } from '../stores/session.store'; // Import session store
import { useSessionStore } from '../stores/session.store';
import { storeToRefs } from 'pinia';
// Removed unused imports: ref, onMounted, onUnmounted, watch, useSettingsStore
// Removed unused interfaces: PortInfo, DockerContainer, DockerStats (now in composable)
const { t } = useI18n();
const sessionStore = useSessionStore();
@@ -4,16 +4,15 @@ import { useI18n } from 'vue-i18n';
import { storeToRefs } from 'pinia';
import MonacoEditor from './MonacoEditor.vue';
import FileEditorTabs from './FileEditorTabs.vue';
import { useFileEditorStore, type FileTab } from '../stores/fileEditor.store'; // 导入 FileTab 类型
import { useFileEditorStore, type FileTab } from '../stores/fileEditor.store';
import { useSettingsStore } from '../stores/settings.store';
import { useSessionStore } from '../stores/session.store'; // 导入 Session Store
import type { EditorFileContent, SaveStatus } from '../types/sftp.types';
import { getLanguageFromFilename, getFilenameFromPath } from '../stores/fileEditor.store';
import { useSessionStore } from '../stores/session.store';
const { t } = useI18n();
const fileEditorStore = useFileEditorStore();
const settingsStore = useSettingsStore();
const sessionStore = useSessionStore(); // 实例化 Session Store
const sessionStore = useSessionStore();
// --- 本地状态控制弹窗显示 ---
const isVisible = ref(false);
@@ -1,7 +1,7 @@
<script setup lang="ts">
import type { PropType } from 'vue';
import { useI18n } from 'vue-i18n';
import type { FileTab } from '../stores/fileEditor.store'; // 导入 FileTab 类型
import type { FileTab } from '../stores/fileEditor.store';
defineProps({
tabs: {
@@ -1,31 +1,24 @@
<script setup lang="ts">
import { ref, computed, onMounted, onBeforeUnmount, nextTick, watch, watchEffect, type PropType, readonly, defineExpose, shallowRef } from 'vue';
// 移除 debounce 导入
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router'; // 保留用于生成下载 URL (如果下载逻辑移动则可移除)
import { storeToRefs } from 'pinia'; // 导入 storeToRefs
// 导入 SFTP Actions 工厂函数和所需的类型
import { useRoute } from 'vue-router';
import { storeToRefs } from 'pinia';
import { createSftpActionsManager, type WebSocketDependencies } from '../composables/useSftpActions';
import { useFileUploader } from '../composables/useFileUploader';
// import { useFileEditor } from '../composables/useFileEditor'; // 移除旧的 composable 导入
import { useFileEditorStore, type FileInfo } from '../stores/fileEditor.store'; // 导入新的 Store 和 FileInfo 类型
import { useFileEditorStore, type FileInfo } from '../stores/fileEditor.store';
import { useSessionStore } from '../stores/session.store';
import { useSettingsStore } from '../stores/settings.store'; // +++ 实例化 Settings Store +++
import { useFocusSwitcherStore } from '../stores/focusSwitcher.store'; // +++ 实例化焦点切换 Store +++
import { useFileManagerContextMenu, type ClipboardState } from '../composables/file-manager/useFileManagerContextMenu'; // +++ 导入上下文菜单 Composable 和 ClipboardState +++
import { useFileManagerSelection } from '../composables/file-manager/useFileManagerSelection'; // +++ 导入选择 Composable +++
import { useFileManagerDragAndDrop } from '../composables/file-manager/useFileManagerDragAndDrop'; // +++ 导入拖放 Composable +++
import { useFileManagerKeyboardNavigation } from '../composables/file-manager/useFileManagerKeyboardNavigation'; // +++ 导入键盘导航 Composable +++
// WebSocket composable 不再直接使用
import { useSettingsStore } from '../stores/settings.store';
import { useFocusSwitcherStore } from '../stores/focusSwitcher.store';
import { useFileManagerContextMenu, type ClipboardState } from '../composables/file-manager/useFileManagerContextMenu';
import { useFileManagerSelection } from '../composables/file-manager/useFileManagerSelection';
import { useFileManagerDragAndDrop } from '../composables/file-manager/useFileManagerDragAndDrop';
import { useFileManagerKeyboardNavigation } from '../composables/file-manager/useFileManagerKeyboardNavigation';
import FileUploadPopup from './FileUploadPopup.vue';
import FileManagerContextMenu from './FileManagerContextMenu.vue'; // +++ 导入上下文菜单组件 +++
// import FileEditorOverlay from './FileEditorOverlay.vue'; // 不再在此处渲染
// 从类型文件导入所需类型
import FileManagerContextMenu from './FileManagerContextMenu.vue';
import type { FileListItem } from '../types/sftp.types';
// 从 websocket 类型文件导入所需类型
import type { WebSocketMessage } from '../types/websocket.types'; // 导入 WebSocketMessage
import type { WebSocketMessage } from '../types/websocket.types';
// 定义 SftpManagerInstance 类型,基于 createSftpActionsManager 的返回类型
type SftpManagerInstance = ReturnType<typeof createSftpActionsManager>;
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { computed } from 'vue';
import { useI18n } from 'vue-i18n';
import type { UploadItem } from '../types/upload.types'; //
import type { UploadItem } from '../types/upload.types';
const props = defineProps<{
uploads: Record<string, UploadItem>; //
@@ -1,8 +1,8 @@
<script setup lang="ts">
import { ref, computed, watch, reactive, type Ref } from 'vue'; // Ref
import { ref, computed, watch, reactive, type Ref } from 'vue';
import { useI18n } from 'vue-i18n';
import draggable from 'vuedraggable';
import { useFocusSwitcherStore, type FocusableInput, type FocusItemConfig, type FocusSwitcherFullConfig } from '../stores/focusSwitcher.store'; // ++ ++
import { useFocusSwitcherStore, type FocusableInput, type FocusItemConfig, type FocusSwitcherFullConfig } from '../stores/focusSwitcher.store';
import { storeToRefs } from 'pinia';
//
@@ -236,9 +236,6 @@ const clonePane = (paneName: PaneName): LayoutNode => {
// Handle updates from LayoutNodeEditor (for main layout)
const handleNodeUpdate = (updatedNode: LayoutNode) => {
console.log('[LayoutConfigurator] Received node update from editor:', updatedNode);
// Assuming the update is for the root node for simplicity
// v-model on LayoutNodeEditor might handle this, but explicit update is safer
// Update the local tree; isModified will react automatically
localLayoutTree.value = updatedNode;
};
@@ -278,11 +275,6 @@ const handleNodeRemove = (payload: { parentNodeId: string | undefined; nodeIndex
console.log('[LayoutConfigurator] Received node remove request:', payload);
if (payload.parentNodeId === undefined && payload.nodeIndex === 0) {
if (confirm(t('layoutConfigurator.confirmClearLayout', '确定要清空整个布局吗?所有面板将返回可用列表。'))) { // Keep default text for now
// Add all panes from the tree back to available list before clearing - REMOVED, no longer needed
// const usedInTree = getMainLayoutUsedPaneNames(localLayoutTree.value);
// usedInTree.forEach(paneName => addPaneToAvailableList(paneName));
// Clear the tree
// Update the local tree; isModified will react automatically
localLayoutTree.value = null;
}
} else if (payload.parentNodeId) {
@@ -102,23 +102,8 @@ const handleChildUpdate = (updatedChildNode: LayoutNode, index: number) => {
//
const handleChildRemove = (payload: { parentNodeId: string | undefined; nodeIndex: number }) => {
// LayoutConfigurator
console.log(`[LayoutNodeEditor ${props.node.id}] Relaying removeNode event upwards:`, payload); //
console.log(`[LayoutNodeEditor ${props.node.id}] Relaying removeNode event upwards:`, payload);
emit('removeNode', payload);
/*
//
if (payload.parentNodeId === props.node.id && props.node.children) {
const newChildren = [...props.node.children];
newChildren.splice(payload.nodeIndex, 1);
//
//
// emit update:node LayoutConfigurator
emit('update:node', { ...props.node, children: newChildren });
} else {
//
emit('removeNode', payload);
}
*/
};
</script>
@@ -194,28 +179,16 @@ const handleChildRemove = (payload: { parentNodeId: string | undefined; nodeInde
<style scoped>
.layout-node-editor {
/* border: 1px solid var(--border-color);
margin: var(--base-margin);
padding: var(--base-margin);
position: relative;
background-color: var(--header-bg-color);
min-height: 60px;
display: flex;
flex-direction: column; */
}
/* .node-type-container {
background-color: var(--app-bg-color);
}
.node-type-pane {
background-color: var(--app-bg-color);
} */
.node-controls {
display: flex;
justify-content: space-between;
align-items: center;
background-color: var(--header-bg-color); /* Or a specific control bar background */
background-color: var(--header-bg-color);
padding: 3px var(--base-margin);
margin-bottom: var(--base-margin);
font-size: 0.8em;
@@ -6,7 +6,7 @@
import { ref, onMounted, onBeforeUnmount, watch, defineExpose } from 'vue';
import * as monaco from 'monaco-editor';
const localFontSize = ref(14); // <-- 14
const localFontSize = ref(14); // 14
const props = defineProps({
modelValue: {
@@ -1,5 +1,5 @@
<template>
<div class="p-0"> <!-- Remove padding from here, parent view provides it -->
<div class="p-0">
<h2 class="text-xl font-semibold text-foreground mb-4 pb-2 border-b border-border"> <!-- Title styling -->
{{ $t('settings.notifications.title') }}
</h2>
@@ -1,40 +1,40 @@
<script setup lang="ts">
import { ref, reactive, onMounted, watch, computed } from 'vue';
import { useI18n } from 'vue-i18n';
import { useAppearanceStore } from '../stores/appearance.store'; // 使 store
import { useAppearanceStore } from '../stores/appearance.store';
import { storeToRefs } from 'pinia';
import type { ITheme } from 'xterm';
import type { TerminalTheme } from '../types/terminal-theme.types'; //
import { defaultXtermTheme } from '../features/appearance/config/default-themes'; //
import type { TerminalTheme } from '../types/terminal-theme.types';
import { defaultXtermTheme } from '../features/appearance/config/default-themes';
const { t } = useI18n();
const appearanceStore = useAppearanceStore();
const {
appearanceSettings, // <-- ref
appearanceSettings,
currentUiTheme,
// currentTerminalTheme, //
activeTerminalThemeId, // number | null | undefined
allTerminalThemes, // 使
activeTerminalThemeId,
allTerminalThemes,
currentTerminalFontFamily,
currentTerminalFontSize,
currentEditorFontSize, // <--
currentEditorFontSize,
pageBackgroundImage,
// pageBackgroundOpacity, // Removed
terminalBackgroundImage,
// terminalBackgroundOpacity, // Removed
isTerminalBackgroundEnabled, // <--
isTerminalBackgroundEnabled,
} = storeToRefs(appearanceStore);
// --- ---
const editableUiTheme = ref<Record<string, string>>({});
const editableTerminalFontFamily = ref('');
const editableTerminalFontSize = ref(14);
const editableEditorFontSize = ref(14); // <--
// const editablePageBackgroundOpacity = ref(1.0); // Removed
// const editableTerminalBackgroundOpacity = ref(1.0); // Removed
const editableUiThemeString = ref(''); // textarea
const themeParseError = ref<string | null>(null); // JSON
const localTerminalBackgroundEnabled = ref(true); // <--
const editableEditorFontSize = ref(14);
const editableUiThemeString = ref('');
const themeParseError = ref<string | null>(null);
const localTerminalBackgroundEnabled = ref(true);
//
const isEditingTheme = ref(false); //
@@ -1,15 +1,14 @@
<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, watch, nextTick } from 'vue';
import { ITheme } from 'xterm';
import { Terminal, ITerminalAddon, IDisposable } from 'xterm'; // +++ ITerminalAddon IDisposable +++
import { useAppearanceStore } from '../stores/appearance.store'; // store
import { useSettingsStore } from '../stores/settings.store'; // +++ store +++
import { storeToRefs } from 'pinia'; // storeToRefs
import { Terminal, ITerminalAddon, IDisposable } from 'xterm';
import { useAppearanceStore } from '../stores/appearance.store';
import { useSettingsStore } from '../stores/settings.store';
import { storeToRefs } from 'pinia';
import { FitAddon } from 'xterm-addon-fit';
import { WebLinksAddon } from 'xterm-addon-web-links';
import { SearchAddon, type ISearchOptions } from '@xterm/addon-search'; // *** ***
import 'xterm/css/xterm.css'; // xterm
// *** CSS ***
import { SearchAddon, type ISearchOptions } from '@xterm/addon-search';
import 'xterm/css/xterm.css';
// props emits
const props = defineProps<{
@@ -7,7 +7,7 @@ import WorkspaceConnectionListComponent from './WorkspaceConnectionList.vue';
import { useSessionStore } from '../stores/session.store';
import { useConnectionsStore, type ConnectionInfo } from '../stores/connections.store';
import { useLayoutStore, type PaneName } from '../stores/layout.store';
//
import type { SessionTabInfoWithStatus } from '../stores/session.store';
@@ -1,13 +1,13 @@
<script setup lang="ts">
import { ref, computed, onMounted, onBeforeUnmount, defineExpose, watch, nextTick } from 'vue';
import { storeToRefs } from 'pinia';
// import { useRouter } from 'vue-router'; // router
import { useI18n } from 'vue-i18n';
// import RemoteDesktopModal from './RemoteDesktopModal.vue'; // --- RDP ---
import { useConnectionsStore, ConnectionInfo } from '../stores/connections.store';
import { useTagsStore, TagInfo } from '../stores/tags.store';
import { useSessionStore } from '../stores/session.store'; // session store
import { useFocusSwitcherStore } from '../stores/focusSwitcher.store'; // +++ Store +++
import { useSessionStore } from '../stores/session.store';
import { useFocusSwitcherStore } from '../stores/focusSwitcher.store';
//
const emit = defineEmits([
@@ -15,8 +15,7 @@ const emit = defineEmits([
// 'open-new-session', // - ()
'request-add-connection', // -
'request-edit-connection' // -
// --- RDP ---
// 'request-rdp-modal'
]);
const { t } = useI18n();