🚧 Refactor basic services
This commit is contained in:
@@ -7,7 +7,7 @@ import SettingSection from '../components/SettingSection.vue';
|
||||
import SettingItem from '../components/SettingItem.vue';
|
||||
import { SystemThemeType, LanguageType } from '@/../bindings/voidraft/internal/models/models';
|
||||
import { createDebounce } from '@/common/utils/debounce';
|
||||
import { createTimerManager } from '@/common/utils/timerUtils';
|
||||
import { useConfirm } from '@/composables/useConfirm';
|
||||
import PickColors from 'vue-pick-colors';
|
||||
import type { ThemeColors } from '@/views/editor/theme/types';
|
||||
|
||||
@@ -21,31 +21,22 @@ const { debouncedFn: debouncedUpdateColor } = createDebounce(
|
||||
{ delay: 100 }
|
||||
);
|
||||
|
||||
const { debouncedFn: debouncedResetTheme } = createDebounce(
|
||||
async () => {
|
||||
const success = await themeStore.resetCurrentTheme();
|
||||
|
||||
if (success) {
|
||||
// 重新加载临时颜色
|
||||
syncTempColors();
|
||||
hasUnsavedChanges.value = false;
|
||||
}
|
||||
},
|
||||
{ delay: 300 }
|
||||
);
|
||||
|
||||
// 创建定时器管理器
|
||||
const resetTimer = createTimerManager();
|
||||
|
||||
// 临时颜色状态(用于编辑)
|
||||
const tempColors = ref<ThemeColors | null>(null);
|
||||
|
||||
// 标记是否有未保存的更改
|
||||
const hasUnsavedChanges = ref(false);
|
||||
|
||||
// 重置按钮状态
|
||||
const resetButtonState = ref({
|
||||
confirming: false
|
||||
// 重置主题确认
|
||||
const { isConfirming: isResetConfirming, requestConfirm: requestResetConfirm } = useConfirm({
|
||||
timeout: 3000,
|
||||
onConfirm: async () => {
|
||||
const success = await themeStore.resetCurrentTheme();
|
||||
if (success) {
|
||||
syncTempColors();
|
||||
hasUnsavedChanges.value = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 当前选中的主题名称
|
||||
@@ -125,23 +116,6 @@ const toggleSearch = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 处理重置按钮点击
|
||||
const handleResetClick = () => {
|
||||
if (resetButtonState.value.confirming) {
|
||||
|
||||
debouncedResetTheme();
|
||||
|
||||
resetButtonState.value.confirming = false;
|
||||
resetTimer.clear();
|
||||
} else {
|
||||
resetButtonState.value.confirming = true;
|
||||
// 设置3秒后自动恢复
|
||||
resetTimer.set(() => {
|
||||
resetButtonState.value.confirming = false;
|
||||
}, 3000);
|
||||
}
|
||||
};
|
||||
|
||||
// 更新本地颜色配置
|
||||
const updateLocalColor = (colorKey: string, value: string) => {
|
||||
if (!tempColors.value) return;
|
||||
@@ -295,10 +269,10 @@ const handlePickerClose = () => {
|
||||
</button>
|
||||
<button
|
||||
v-if="!hasUnsavedChanges"
|
||||
:class="['reset-button', resetButtonState.confirming ? 'reset-button-confirming' : '']"
|
||||
@click="handleResetClick"
|
||||
:class="['reset-button', isResetConfirming('theme') ? 'reset-button-confirming' : '']"
|
||||
@click="requestResetConfirm('theme')"
|
||||
>
|
||||
{{ resetButtonState.confirming ? t('settings.confirmReset') : t('settings.resetToDefault') }}
|
||||
{{ isResetConfirming('theme') ? t('settings.confirmReset') : t('settings.resetToDefault') }}
|
||||
</button>
|
||||
<template v-else>
|
||||
<button class="apply-button" @click="applyChanges">
|
||||
|
||||
@@ -4,7 +4,6 @@ import {useI18n} from 'vue-i18n';
|
||||
import {useEditorStore} from '@/stores/editorStore';
|
||||
import {useExtensionStore} from '@/stores/extensionStore';
|
||||
import {ExtensionService} from '@/../bindings/voidraft/internal/services';
|
||||
import {ExtensionID} from '@/../bindings/voidraft/internal/models/models';
|
||||
import {
|
||||
getAllExtensionIds,
|
||||
getExtensionDefaultConfig,
|
||||
@@ -21,59 +20,58 @@ const editorStore = useEditorStore();
|
||||
const extensionStore = useExtensionStore();
|
||||
|
||||
// 展开状态管理
|
||||
const expandedExtensions = ref<Set<ExtensionID>>(new Set());
|
||||
const expandedExtensions = ref<Set<string>>(new Set());
|
||||
|
||||
// 获取所有可用的扩展
|
||||
const availableExtensions = computed(() => {
|
||||
return getAllExtensionIds().map(id => {
|
||||
const extension = extensionStore.extensions.find(ext => ext.id === id);
|
||||
return getAllExtensionIds().map(key => {
|
||||
const extension = extensionStore.extensions.find(ext => ext.key === key);
|
||||
return {
|
||||
id,
|
||||
displayName: getExtensionDisplayName(id),
|
||||
description: getExtensionDescription(id),
|
||||
id: key,
|
||||
displayName: getExtensionDisplayName(key),
|
||||
description: getExtensionDescription(key),
|
||||
enabled: extension?.enabled || false,
|
||||
isDefault: extension?.isDefault || false,
|
||||
hasConfig: hasExtensionConfig(id),
|
||||
hasConfig: hasExtensionConfig(key),
|
||||
config: extension?.config || {},
|
||||
defaultConfig: getExtensionDefaultConfig(id)
|
||||
defaultConfig: getExtensionDefaultConfig(key)
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
// 切换展开状态
|
||||
const toggleExpanded = (extensionId: ExtensionID) => {
|
||||
if (expandedExtensions.value.has(extensionId)) {
|
||||
expandedExtensions.value.delete(extensionId);
|
||||
const toggleExpanded = (extensionKey: string) => {
|
||||
if (expandedExtensions.value.has(extensionKey)) {
|
||||
expandedExtensions.value.delete(extensionKey);
|
||||
} else {
|
||||
expandedExtensions.value.add(extensionId);
|
||||
expandedExtensions.value.add(extensionKey);
|
||||
}
|
||||
};
|
||||
|
||||
// 更新扩展状态
|
||||
const updateExtension = async (extensionId: ExtensionID, enabled: boolean) => {
|
||||
const updateExtension = async (extensionKey: string, enabled: boolean) => {
|
||||
try {
|
||||
await editorStore.updateExtension(extensionId, enabled);
|
||||
await editorStore.updateExtension(extensionKey, enabled);
|
||||
} catch (error) {
|
||||
console.error('Failed to update extension:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 更新扩展配置
|
||||
const updateExtensionConfig = async (extensionId: ExtensionID, configKey: string, value: any) => {
|
||||
const updateExtensionConfig = async (extensionKey: string, configKey: string, value: any) => {
|
||||
try {
|
||||
// 获取当前扩展状态
|
||||
const extension = extensionStore.extensions.find(ext => ext.id === extensionId);
|
||||
const extension = extensionStore.extensions.find(ext => ext.key === extensionKey);
|
||||
if (!extension) return;
|
||||
|
||||
// 更新配置
|
||||
const updatedConfig = {...extension.config};
|
||||
const updatedConfig = {...(extension.config || {})};
|
||||
if (value === undefined) {
|
||||
delete updatedConfig[configKey];
|
||||
} else {
|
||||
updatedConfig[configKey] = value;
|
||||
}
|
||||
// 使用editorStore的updateExtension方法更新,确保应用到所有编辑器实例
|
||||
await editorStore.updateExtension(extensionId, extension.enabled, updatedConfig);
|
||||
await editorStore.updateExtension(extensionKey, extension.enabled ?? false, updatedConfig);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Failed to update extension config:', error);
|
||||
@@ -81,19 +79,19 @@ const updateExtensionConfig = async (extensionId: ExtensionID, configKey: string
|
||||
};
|
||||
|
||||
// 重置扩展到默认配置
|
||||
const resetExtension = async (extensionId: ExtensionID) => {
|
||||
const resetExtension = async (extensionKey: string) => {
|
||||
try {
|
||||
// 重置到默认配置
|
||||
await ExtensionService.ResetExtensionToDefault(extensionId);
|
||||
await ExtensionService.ResetExtensionConfig(extensionKey);
|
||||
|
||||
// 重新加载扩展状态以获取最新配置
|
||||
await extensionStore.loadExtensions();
|
||||
|
||||
// 获取重置后的状态,立即应用到所有编辑器视图
|
||||
const extension = extensionStore.extensions.find(ext => ext.id === extensionId);
|
||||
const extension = extensionStore.extensions.find(ext => ext.key === extensionKey);
|
||||
if (extension) {
|
||||
// 通过editorStore更新,确保所有视图都能同步
|
||||
await editorStore.updateExtension(extensionId, extension.enabled, extension.config);
|
||||
await editorStore.updateExtension(extensionKey, extension.enabled ?? false, extension.config);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to reset extension:', error);
|
||||
@@ -127,7 +125,7 @@ const formatConfigValue = (value: any): string => {
|
||||
|
||||
|
||||
const handleConfigInput = async (
|
||||
extensionId: ExtensionID,
|
||||
extensionKey: string,
|
||||
configKey: string,
|
||||
defaultValue: any,
|
||||
event: Event
|
||||
@@ -137,15 +135,15 @@ const handleConfigInput = async (
|
||||
const rawValue = target.value;
|
||||
const trimmedValue = rawValue.trim();
|
||||
if (!trimmedValue.length) {
|
||||
await updateExtensionConfig(extensionId, configKey, undefined);
|
||||
await updateExtensionConfig(extensionKey, configKey, undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const parsedValue = JSON.parse(trimmedValue);
|
||||
await updateExtensionConfig(extensionId, configKey, parsedValue);
|
||||
await updateExtensionConfig(extensionKey, configKey, parsedValue);
|
||||
} catch (_error) {
|
||||
const extension = extensionStore.extensions.find(ext => ext.id === extensionId);
|
||||
const extension = extensionStore.extensions.find(ext => ext.key === extensionKey);
|
||||
const fallbackValue = getConfigValue(extension?.config, configKey, defaultValue);
|
||||
target.value = formatConfigValue(fallbackValue);
|
||||
|
||||
|
||||
@@ -2,133 +2,67 @@
|
||||
import {useConfigStore} from '@/stores/configStore';
|
||||
import {useTabStore} from '@/stores/tabStore';
|
||||
import {useI18n} from 'vue-i18n';
|
||||
import {computed, onUnmounted, ref} from 'vue';
|
||||
import {computed, ref} from 'vue';
|
||||
import SettingSection from '../components/SettingSection.vue';
|
||||
import SettingItem from '../components/SettingItem.vue';
|
||||
import ToggleSwitch from '../components/ToggleSwitch.vue';
|
||||
import {
|
||||
DialogService,
|
||||
MigrationProgress,
|
||||
MigrationService,
|
||||
MigrationStatus
|
||||
} from '@/../bindings/voidraft/internal/services';
|
||||
import {DialogService, MigrationService} from '@/../bindings/voidraft/internal/services';
|
||||
import {useSystemStore} from "@/stores/systemStore";
|
||||
import {useConfirm, usePolling} from '@/composables';
|
||||
|
||||
const {t} = useI18n();
|
||||
const configStore = useConfigStore();
|
||||
const systemStore = useSystemStore();
|
||||
const tabStore = useTabStore();
|
||||
// 迁移进度状态
|
||||
const migrationProgress = ref<MigrationProgress>(new MigrationProgress({
|
||||
status: MigrationStatus.MigrationStatusCompleted,
|
||||
progress: 0
|
||||
}));
|
||||
|
||||
// 轮询相关
|
||||
let pollingTimer: number | null = null;
|
||||
const isPolling = ref(false);
|
||||
|
||||
// 进度条显示控制
|
||||
const showProgress = ref(false);
|
||||
const progressError = ref('');
|
||||
let hideProgressTimer: any = null;
|
||||
const showBar = ref(false);
|
||||
const manualError = ref(''); // 用于捕获 MigrateDirectory 抛出的错误
|
||||
let hideTimer = 0;
|
||||
|
||||
// 开始轮询迁移进度
|
||||
const startPolling = () => {
|
||||
if (isPolling.value) return;
|
||||
|
||||
isPolling.value = true;
|
||||
showProgress.value = true;
|
||||
progressError.value = '';
|
||||
|
||||
// 立即重置迁移进度状态,避免从之前的失败状态渐变
|
||||
migrationProgress.value = new MigrationProgress({
|
||||
status: MigrationStatus.MigrationStatusMigrating,
|
||||
progress: 0
|
||||
});
|
||||
|
||||
pollingTimer = window.setInterval(async () => {
|
||||
try {
|
||||
const progress = await MigrationService.GetProgress();
|
||||
migrationProgress.value = progress;
|
||||
|
||||
const {status, error} = progress;
|
||||
const isCompleted = [MigrationStatus.MigrationStatusCompleted, MigrationStatus.MigrationStatusFailed].includes(status);
|
||||
|
||||
if (isCompleted) {
|
||||
stopPolling();
|
||||
|
||||
// 设置错误信息(如果是失败状态)
|
||||
progressError.value = (status === MigrationStatus.MigrationStatusFailed) ? (error || 'Migration failed') : '';
|
||||
|
||||
const delay = status === MigrationStatus.MigrationStatusCompleted ? 3000 : 5000;
|
||||
hideProgressTimer = setTimeout(hideProgress, delay);
|
||||
// 轮询迁移进度
|
||||
const {data: progress, error: pollError, isActive: migrating, start, stop, reset} = usePolling(
|
||||
() => MigrationService.GetProgress(),
|
||||
{
|
||||
interval: 300,
|
||||
shouldStop: ({progress, error}) => !!error || progress >= 100,
|
||||
onStop: () => {
|
||||
const hasError = pollError.value || progress.value?.error;
|
||||
hideTimer = window.setTimeout(hideAll, hasError ? 5000 : 3000);
|
||||
}
|
||||
} catch (_error) {
|
||||
stopPolling();
|
||||
|
||||
// 使用常量简化错误处理
|
||||
const errorMsg = 'Failed to get migration progress';
|
||||
Object.assign(migrationProgress.value, {
|
||||
status: MigrationStatus.MigrationStatusFailed,
|
||||
progress: 0,
|
||||
error: errorMsg
|
||||
});
|
||||
progressError.value = errorMsg;
|
||||
|
||||
hideProgressTimer = setTimeout(hideProgress, 5000);
|
||||
}
|
||||
}, 200);
|
||||
};
|
||||
|
||||
// 停止轮询
|
||||
const stopPolling = () => {
|
||||
if (pollingTimer) {
|
||||
clearInterval(pollingTimer);
|
||||
pollingTimer = null;
|
||||
}
|
||||
isPolling.value = false;
|
||||
};
|
||||
|
||||
// 隐藏进度条
|
||||
const hideProgress = () => {
|
||||
showProgress.value = false;
|
||||
progressError.value = '';
|
||||
|
||||
// 重置迁移状态,避免下次显示时状态不正确
|
||||
migrationProgress.value = new MigrationProgress({
|
||||
status: MigrationStatus.MigrationStatusCompleted,
|
||||
progress: 0
|
||||
});
|
||||
|
||||
if (hideProgressTimer) {
|
||||
clearTimeout(hideProgressTimer);
|
||||
hideProgressTimer = null;
|
||||
}
|
||||
};
|
||||
|
||||
// 简化的迁移状态管理
|
||||
const isMigrating = computed(() => migrationProgress.value.status === MigrationStatus.MigrationStatusMigrating);
|
||||
|
||||
// 进度条样式 - 使用 Map 简化条件判断
|
||||
const statusClassMap = new Map([
|
||||
[MigrationStatus.MigrationStatusMigrating, 'migrating'],
|
||||
[MigrationStatus.MigrationStatusCompleted, 'success'],
|
||||
[MigrationStatus.MigrationStatusFailed, 'error']
|
||||
]);
|
||||
|
||||
const progressBarClass = computed(() =>
|
||||
showProgress.value ? statusClassMap.get(migrationProgress.value.status) ?? '' : ''
|
||||
);
|
||||
|
||||
const progressBarWidth = computed(() => {
|
||||
if (!showProgress.value) return '0%';
|
||||
return isMigrating.value ? `${migrationProgress.value.progress}%` : '100%';
|
||||
// 派生状态
|
||||
const migrationError = computed(() => manualError.value || pollError.value || progress.value?.error || '');
|
||||
const currentProgress = computed(() => progress.value?.progress ?? 0);
|
||||
|
||||
const barClass = computed(() => {
|
||||
if (!showBar.value) return '';
|
||||
return migrationError.value ? 'error' : currentProgress.value >= 100 ? 'success' : 'migrating';
|
||||
});
|
||||
|
||||
// 重置确认状态
|
||||
const resetConfirmState = ref<'idle' | 'confirming'>('idle');
|
||||
let resetConfirmTimer: any = null;
|
||||
const barWidth = computed(() => {
|
||||
if (!showBar.value) return '0%';
|
||||
return (migrationError.value || currentProgress.value >= 100) ? '100%' : `${currentProgress.value}%`;
|
||||
});
|
||||
|
||||
// 隐藏进度条并清除所有状态
|
||||
const hideAll = () => {
|
||||
clearTimeout(hideTimer);
|
||||
hideTimer = 0;
|
||||
showBar.value = false;
|
||||
manualError.value = '';
|
||||
reset(); // 清除轮询状态
|
||||
};
|
||||
|
||||
// 重置设置确认
|
||||
const {isConfirming: isResetConfirming, requestConfirm: requestResetConfirm} = useConfirm({
|
||||
timeout: 3000,
|
||||
onConfirm: async () => {
|
||||
await configStore.resetConfig();
|
||||
}
|
||||
});
|
||||
|
||||
// 可选键列表
|
||||
const keyOptions = [
|
||||
@@ -201,7 +135,7 @@ const modifierKeys = computed(() => ({
|
||||
win: configStore.config.general.globalHotkey.win
|
||||
}));
|
||||
|
||||
// 主键配置 - 只读计算属性
|
||||
// 主键配置
|
||||
const selectedKey = computed(() => configStore.config.general.globalHotkey.key);
|
||||
|
||||
// 切换修饰键
|
||||
@@ -218,29 +152,8 @@ const updateSelectedKey = (event: Event) => {
|
||||
configStore.setGlobalHotkey(newHotkey);
|
||||
};
|
||||
|
||||
// 重置设置
|
||||
const resetSettings = () => {
|
||||
if (resetConfirmState.value === 'idle') {
|
||||
// 第一次点击,进入确认状态
|
||||
resetConfirmState.value = 'confirming';
|
||||
// 3秒后自动返回idle状态
|
||||
resetConfirmTimer = setTimeout(() => {
|
||||
resetConfirmState.value = 'idle';
|
||||
}, 3000);
|
||||
} else if (resetConfirmState.value === 'confirming') {
|
||||
// 第二次点击,执行重置
|
||||
clearTimeout(resetConfirmTimer);
|
||||
resetConfirmState.value = 'idle';
|
||||
confirmReset();
|
||||
}
|
||||
};
|
||||
|
||||
// 确认重置
|
||||
const confirmReset = async () => {
|
||||
await configStore.resetConfig();
|
||||
};
|
||||
|
||||
// 计算热键预览文本 - 使用现代语法简化
|
||||
// 计算热键预览文本
|
||||
const hotkeyPreview = computed(() => {
|
||||
if (!enableGlobalHotkey.value) return '';
|
||||
|
||||
@@ -261,54 +174,30 @@ const currentDataPath = computed(() => configStore.config.general.dataPath);
|
||||
|
||||
// 选择数据存储目录
|
||||
const selectDataDirectory = async () => {
|
||||
if (isMigrating.value) return;
|
||||
|
||||
if (migrating.value) return;
|
||||
|
||||
const selectedPath = await DialogService.SelectDirectory();
|
||||
if (!selectedPath?.trim() || selectedPath === currentDataPath.value) return;
|
||||
|
||||
// 检查用户是否取消了选择或路径为空
|
||||
if (!selectedPath || !selectedPath.trim() || selectedPath === currentDataPath.value) {
|
||||
return;
|
||||
}
|
||||
const oldPath = currentDataPath.value;
|
||||
const newPath = selectedPath.trim();
|
||||
const [oldPath, newPath] = [currentDataPath.value, selectedPath.trim()];
|
||||
|
||||
// 清除之前的进度状态
|
||||
hideProgress();
|
||||
// 清除之前的状态并开始轮询
|
||||
hideAll();
|
||||
showBar.value = true;
|
||||
manualError.value = '';
|
||||
start();
|
||||
|
||||
// 开始轮询迁移进度
|
||||
startPolling();
|
||||
|
||||
// 开始迁移
|
||||
try {
|
||||
await MigrationService.MigrateDirectory(oldPath, newPath);
|
||||
await configStore.setDataPath(newPath);
|
||||
} catch (error) {
|
||||
stopPolling();
|
||||
|
||||
// 使用解构和默认值简化错误处理
|
||||
const errorMsg = error?.toString() || 'Migration failed';
|
||||
showProgress.value = true;
|
||||
|
||||
Object.assign(migrationProgress.value, {
|
||||
status: MigrationStatus.MigrationStatusFailed,
|
||||
progress: 0,
|
||||
error: errorMsg
|
||||
});
|
||||
progressError.value = errorMsg;
|
||||
|
||||
hideProgressTimer = setTimeout(hideProgress, 5000);
|
||||
} catch (e) {
|
||||
stop();
|
||||
// 设置手动捕获的错误(当轮询还没获取到错误时)
|
||||
manualError.value = String(e).replace(/^Error:\s*/i, '') || 'Migration failed';
|
||||
showBar.value = true;
|
||||
hideTimer = window.setTimeout(hideAll, 5000);
|
||||
}
|
||||
};
|
||||
|
||||
// 清理定时器
|
||||
onUnmounted(() => {
|
||||
stopPolling();
|
||||
hideProgress();
|
||||
if (resetConfirmTimer) {
|
||||
clearTimeout(resetConfirmTimer);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -394,24 +283,15 @@ onUnmounted(() => {
|
||||
class="path-display-input"
|
||||
@click="selectDataDirectory"
|
||||
:title="t('settings.clickToSelectPath')"
|
||||
:disabled="isMigrating"
|
||||
:disabled="migrating"
|
||||
/>
|
||||
<!-- 简洁的进度条 -->
|
||||
<div
|
||||
class="progress-bar"
|
||||
:class="[
|
||||
{ 'active': showProgress },
|
||||
progressBarClass
|
||||
]"
|
||||
:style="{ width: progressBarWidth }"
|
||||
></div>
|
||||
<!-- 进度条 -->
|
||||
<div class="progress-bar" :class="[{'active': showBar}, barClass]" :style="{width: barWidth}"/>
|
||||
</div>
|
||||
|
||||
<!-- 错误提示 -->
|
||||
<Transition name="error-fade">
|
||||
<div v-if="progressError" class="progress-error">
|
||||
{{ progressError }}
|
||||
</div>
|
||||
<div v-if="migrationError" class="progress-error">{{ migrationError }}</div>
|
||||
</Transition>
|
||||
</div>
|
||||
</div>
|
||||
@@ -421,15 +301,10 @@ onUnmounted(() => {
|
||||
<SettingItem :title="t('settings.resetAllSettings')">
|
||||
<button
|
||||
class="reset-button"
|
||||
:class="{ 'confirming': resetConfirmState === 'confirming' }"
|
||||
@click="resetSettings"
|
||||
:class="{ 'confirming': isResetConfirming('reset') }"
|
||||
@click="requestResetConfirm('reset')"
|
||||
>
|
||||
<template v-if="resetConfirmState === 'idle'">
|
||||
{{ t('settings.reset') }}
|
||||
</template>
|
||||
<template v-else-if="resetConfirmState === 'confirming'">
|
||||
{{ t('settings.confirmReset') }}
|
||||
</template>
|
||||
{{ isResetConfirming('reset') ? t('settings.confirmReset') : t('settings.reset') }}
|
||||
</button>
|
||||
</SettingItem>
|
||||
</SettingSection>
|
||||
@@ -656,11 +531,8 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
.progress-error {
|
||||
margin-top: 6px;
|
||||
font-size: 12px;
|
||||
color: #ef4444;
|
||||
padding: 0 2px;
|
||||
line-height: 1.4;
|
||||
opacity: 1;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useKeybindingStore } from '@/stores/keybindingStore';
|
||||
import { useExtensionStore } from '@/stores/extensionStore';
|
||||
import { useSystemStore } from '@/stores/systemStore';
|
||||
import { getCommandDescription } from '@/views/editor/keymap/commands';
|
||||
import {KeyBindingCommand} from "@/../bindings/voidraft/internal/models";
|
||||
import { KeyBindingKey } from '@/../bindings/voidraft/internal/models/models';
|
||||
|
||||
const { t } = useI18n();
|
||||
const keybindingStore = useKeybindingStore();
|
||||
@@ -25,21 +25,21 @@ const keyBindings = computed(() => {
|
||||
const enabledExtensionIds = new Set(extensionStore.enabledExtensionIds);
|
||||
|
||||
return keybindingStore.keyBindings
|
||||
.filter(kb => kb.enabled && enabledExtensionIds.has(kb.extension))
|
||||
.filter(kb => kb.enabled && (!kb.extension || enabledExtensionIds.has(kb.extension)))
|
||||
.map(kb => ({
|
||||
id: kb.command,
|
||||
keys: parseKeyBinding(kb.key, kb.command),
|
||||
category: kb.extension,
|
||||
description: getCommandDescription(kb.command) || kb.command
|
||||
id: kb.key,
|
||||
keys: parseKeyBinding(kb.command || '', kb.key),
|
||||
category: kb.extension || '',
|
||||
description: kb.key ? (getCommandDescription(kb.key) || kb.key) : ''
|
||||
}));
|
||||
});
|
||||
|
||||
// 解析快捷键字符串为显示数组
|
||||
const parseKeyBinding = (keyStr: string, command?: string): string[] => {
|
||||
const parseKeyBinding = (keyStr: string, keyBindingKey?: string): string[] => {
|
||||
if (!keyStr) return [];
|
||||
|
||||
// 特殊处理重做快捷键的操作系统差异
|
||||
if (command === KeyBindingCommand.HistoryRedoCommand && keyStr === 'Mod-Shift-z') {
|
||||
if (keyBindingKey === KeyBindingKey.HistoryRedoKeyBindingKey && keyStr === 'Mod-Shift-z') {
|
||||
if (systemStore.isMacOS) {
|
||||
return ['⌘', '⇧', 'Z']; // macOS: Cmd+Shift+Z
|
||||
} else {
|
||||
@@ -48,7 +48,7 @@ const parseKeyBinding = (keyStr: string, command?: string): string[] => {
|
||||
}
|
||||
|
||||
// 特殊处理重做选择快捷键的操作系统差异
|
||||
if (command === KeyBindingCommand.HistoryRedoSelectionCommand && keyStr === 'Mod-Shift-u') {
|
||||
if (keyBindingKey === KeyBindingKey.HistoryRedoSelectionKeyBindingKey && keyStr === 'Mod-Shift-u') {
|
||||
if (systemStore.isMacOS) {
|
||||
return ['⌘', '⇧', 'U']; // macOS: Cmd+Shift+U
|
||||
} else {
|
||||
@@ -57,7 +57,7 @@ const parseKeyBinding = (keyStr: string, command?: string): string[] => {
|
||||
}
|
||||
|
||||
// 特殊处理代码折叠快捷键的操作系统差异
|
||||
if (command === KeyBindingCommand.FoldCodeCommand && keyStr === 'Ctrl-Shift-[') {
|
||||
if (keyBindingKey === KeyBindingKey.FoldCodeKeyBindingKey && keyStr === 'Ctrl-Shift-[') {
|
||||
if (systemStore.isMacOS) {
|
||||
return ['⌘', '⌥', '[']; // macOS: Cmd+Alt+[
|
||||
} else {
|
||||
@@ -65,7 +65,7 @@ const parseKeyBinding = (keyStr: string, command?: string): string[] => {
|
||||
}
|
||||
}
|
||||
|
||||
if (command === KeyBindingCommand.UnfoldCodeCommand && keyStr === 'Ctrl-Shift-]') {
|
||||
if (keyBindingKey === KeyBindingKey.UnfoldCodeKeyBindingKey && keyStr === 'Ctrl-Shift-]') {
|
||||
if (systemStore.isMacOS) {
|
||||
return ['⌘', '⌥', ']']; // macOS: Cmd+Alt+]
|
||||
} else {
|
||||
@@ -74,7 +74,7 @@ const parseKeyBinding = (keyStr: string, command?: string): string[] => {
|
||||
}
|
||||
|
||||
// 特殊处理编辑快捷键的操作系统差异
|
||||
if (command === KeyBindingCommand.CursorSyntaxLeftCommand && keyStr === 'Alt-ArrowLeft') {
|
||||
if (keyBindingKey === KeyBindingKey.CursorSyntaxLeftKeyBindingKey && keyStr === 'Alt-ArrowLeft') {
|
||||
if (systemStore.isMacOS) {
|
||||
return ['Ctrl', '←']; // macOS: Ctrl+ArrowLeft
|
||||
} else {
|
||||
@@ -82,7 +82,7 @@ const parseKeyBinding = (keyStr: string, command?: string): string[] => {
|
||||
}
|
||||
}
|
||||
|
||||
if (command === KeyBindingCommand.CursorSyntaxRightCommand && keyStr === 'Alt-ArrowRight') {
|
||||
if (keyBindingKey === KeyBindingKey.CursorSyntaxRightKeyBindingKey && keyStr === 'Alt-ArrowRight') {
|
||||
if (systemStore.isMacOS) {
|
||||
return ['Ctrl', '→']; // macOS: Ctrl+ArrowRight
|
||||
} else {
|
||||
@@ -90,7 +90,7 @@ const parseKeyBinding = (keyStr: string, command?: string): string[] => {
|
||||
}
|
||||
}
|
||||
|
||||
if (command === KeyBindingCommand.InsertBlankLineCommand && keyStr === 'Ctrl-Enter') {
|
||||
if (keyBindingKey === KeyBindingKey.InsertBlankLineKeyBindingKey && keyStr === 'Ctrl-Enter') {
|
||||
if (systemStore.isMacOS) {
|
||||
return ['⌘', 'Enter']; // macOS: Cmd+Enter
|
||||
} else {
|
||||
@@ -98,7 +98,7 @@ const parseKeyBinding = (keyStr: string, command?: string): string[] => {
|
||||
}
|
||||
}
|
||||
|
||||
if (command === KeyBindingCommand.SelectLineCommand && keyStr === 'Alt-l') {
|
||||
if (keyBindingKey === KeyBindingKey.SelectLineKeyBindingKey && keyStr === 'Alt-l') {
|
||||
if (systemStore.isMacOS) {
|
||||
return ['Ctrl', 'L']; // macOS: Ctrl+l
|
||||
} else {
|
||||
@@ -106,7 +106,7 @@ const parseKeyBinding = (keyStr: string, command?: string): string[] => {
|
||||
}
|
||||
}
|
||||
|
||||
if (command === KeyBindingCommand.SelectParentSyntaxCommand && keyStr === 'Ctrl-i') {
|
||||
if (keyBindingKey === KeyBindingKey.SelectParentSyntaxKeyBindingKey && keyStr === 'Ctrl-i') {
|
||||
if (systemStore.isMacOS) {
|
||||
return ['⌘', 'I']; // macOS: Cmd+i
|
||||
} else {
|
||||
@@ -114,7 +114,7 @@ const parseKeyBinding = (keyStr: string, command?: string): string[] => {
|
||||
}
|
||||
}
|
||||
|
||||
if (command === KeyBindingCommand.IndentLessCommand && keyStr === 'Ctrl-[') {
|
||||
if (keyBindingKey === KeyBindingKey.IndentLessKeyBindingKey && keyStr === 'Ctrl-[') {
|
||||
if (systemStore.isMacOS) {
|
||||
return ['⌘', '[']; // macOS: Cmd+[
|
||||
} else {
|
||||
@@ -122,7 +122,7 @@ const parseKeyBinding = (keyStr: string, command?: string): string[] => {
|
||||
}
|
||||
}
|
||||
|
||||
if (command === KeyBindingCommand.IndentMoreCommand && keyStr === 'Ctrl-]') {
|
||||
if (keyBindingKey === KeyBindingKey.IndentMoreKeyBindingKey && keyStr === 'Ctrl-]') {
|
||||
if (systemStore.isMacOS) {
|
||||
return ['⌘', ']']; // macOS: Cmd+]
|
||||
} else {
|
||||
@@ -130,7 +130,7 @@ const parseKeyBinding = (keyStr: string, command?: string): string[] => {
|
||||
}
|
||||
}
|
||||
|
||||
if (command === KeyBindingCommand.IndentSelectionCommand && keyStr === 'Ctrl-Alt-\\') {
|
||||
if (keyBindingKey === KeyBindingKey.IndentSelectionKeyBindingKey && keyStr === 'Ctrl-Alt-\\') {
|
||||
if (systemStore.isMacOS) {
|
||||
return ['⌘', '⌥', '\\']; // macOS: Cmd+Alt+\
|
||||
} else {
|
||||
@@ -138,7 +138,7 @@ const parseKeyBinding = (keyStr: string, command?: string): string[] => {
|
||||
}
|
||||
}
|
||||
|
||||
if (command === KeyBindingCommand.CursorMatchingBracketCommand && keyStr === 'Shift-Ctrl-\\') {
|
||||
if (keyBindingKey === KeyBindingKey.CursorMatchingBracketKeyBindingKey && keyStr === 'Shift-Ctrl-\\') {
|
||||
if (systemStore.isMacOS) {
|
||||
return ['⇧', '⌘', '\\']; // macOS: Shift+Cmd+\
|
||||
} else {
|
||||
@@ -146,7 +146,7 @@ const parseKeyBinding = (keyStr: string, command?: string): string[] => {
|
||||
}
|
||||
}
|
||||
|
||||
if (command === KeyBindingCommand.ToggleCommentCommand && keyStr === 'Ctrl-/') {
|
||||
if (keyBindingKey === KeyBindingKey.ToggleCommentKeyBindingKey && keyStr === 'Ctrl-/') {
|
||||
if (systemStore.isMacOS) {
|
||||
return ['⌘', '/']; // macOS: Cmd+/
|
||||
} else {
|
||||
@@ -155,7 +155,7 @@ const parseKeyBinding = (keyStr: string, command?: string): string[] => {
|
||||
}
|
||||
|
||||
// 特殊处理删除快捷键的操作系统差异
|
||||
if (command === KeyBindingCommand.DeleteGroupBackwardCommand && keyStr === 'Ctrl-Backspace') {
|
||||
if (keyBindingKey === KeyBindingKey.DeleteGroupBackwardKeyBindingKey && keyStr === 'Ctrl-Backspace') {
|
||||
if (systemStore.isMacOS) {
|
||||
return ['⌘', 'Backspace']; // macOS: Cmd+Backspace
|
||||
} else {
|
||||
@@ -163,7 +163,7 @@ const parseKeyBinding = (keyStr: string, command?: string): string[] => {
|
||||
}
|
||||
}
|
||||
|
||||
if (command === KeyBindingCommand.DeleteGroupForwardCommand && keyStr === 'Ctrl-Delete') {
|
||||
if (keyBindingKey === KeyBindingKey.DeleteGroupForwardKeyBindingKey && keyStr === 'Ctrl-Delete') {
|
||||
if (systemStore.isMacOS) {
|
||||
return ['⌘', 'Delete']; // macOS: Cmd+Delete
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user