Added key binding service

This commit is contained in:
2025-06-20 13:37:48 +08:00
parent 13072a00a1
commit 85544ba1e4
18 changed files with 1968 additions and 349 deletions

View File

@@ -9,7 +9,7 @@ import {
LanguageType,
SystemThemeType,
TabType,
} from '../../bindings/voidraft/internal/models/models';
} from '@/../bindings/voidraft/internal/models/models';
import {useI18n} from 'vue-i18n';
import {useErrorHandler} from '@/utils/errorHandler';
import {ConfigUtils} from '@/utils/configUtils';
@@ -47,27 +47,27 @@ type NumberConfigKey = 'fontSize' | 'tabSize' | 'lineHeight';
// 配置键映射
const GENERAL_CONFIG_KEY_MAP: GeneralConfigKeyMap = {
alwaysOnTop: 'general.always_on_top',
dataPath: 'general.data_path',
enableSystemTray: 'general.enable_system_tray',
enableGlobalHotkey: 'general.enable_global_hotkey',
globalHotkey: 'general.global_hotkey'
alwaysOnTop: 'general.alwaysOnTop',
dataPath: 'general.dataPath',
enableSystemTray: 'general.enableSystemTray',
enableGlobalHotkey: 'general.enableGlobalHotkey',
globalHotkey: 'general.globalHotkey'
} as const;
const EDITING_CONFIG_KEY_MAP: EditingConfigKeyMap = {
fontSize: 'editing.font_size',
fontFamily: 'editing.font_family',
fontWeight: 'editing.font_weight',
lineHeight: 'editing.line_height',
enableTabIndent: 'editing.enable_tab_indent',
tabSize: 'editing.tab_size',
tabType: 'editing.tab_type',
autoSaveDelay: 'editing.auto_save_delay'
fontSize: 'editing.fontSize',
fontFamily: 'editing.fontFamily',
fontWeight: 'editing.fontWeight',
lineHeight: 'editing.lineHeight',
enableTabIndent: 'editing.enableTabIndent',
tabSize: 'editing.tabSize',
tabType: 'editing.tabType',
autoSaveDelay: 'editing.autoSaveDelay'
} as const;
const APPEARANCE_CONFIG_KEY_MAP: AppearanceConfigKeyMap = {
language: 'appearance.language',
systemTheme: 'appearance.system_theme'
systemTheme: 'appearance.systemTheme'
} as const;
// 配置限制
@@ -141,11 +141,9 @@ const DEFAULT_CONFIG: AppConfig = {
language: LanguageType.LangZhCN,
systemTheme: SystemThemeType.SystemThemeDark
},
keyBindings: {},
updates: {},
metadata: {
version: '1.0.0',
lastUpdated: null
lastUpdated: new Date().toString()
}
};
@@ -226,7 +224,6 @@ export const useConfigStore = defineStore('config', () => {
if (appConfig.general) Object.assign(state.config.general, appConfig.general);
if (appConfig.editing) Object.assign(state.config.editing, appConfig.editing);
if (appConfig.appearance) Object.assign(state.config.appearance, appConfig.appearance);
if (appConfig.keyBindings) Object.assign(state.config.keyBindings, appConfig.keyBindings);
if (appConfig.updates) Object.assign(state.config.updates, appConfig.updates);
if (appConfig.metadata) Object.assign(state.config.metadata, appConfig.metadata);
}

View File

@@ -83,7 +83,7 @@ const goBackToEditor = async () => {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
.settings-sidebar {
width: 200px;
width: 180px;
height: 100%;
background-color: var(--settings-card-bg);
border-right: 1px solid var(--settings-border);
@@ -92,7 +92,7 @@ const goBackToEditor = async () => {
box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1);
.settings-header {
padding: 20px 16px;
padding: 16px 14px;
border-bottom: 1px solid var(--settings-border);
background-color: var(--settings-card-bg);
@@ -125,7 +125,7 @@ const goBackToEditor = async () => {
}
h1 {
font-size: 18px;
font-size: 16px;
font-weight: 600;
margin: 0;
color: var(--settings-text);
@@ -138,10 +138,10 @@ const goBackToEditor = async () => {
padding: 10px 0;
overflow-y: auto;
.nav-item {
.nav-item {
display: flex;
align-items: center;
padding: 12px 16px;
padding: 10px 14px;
cursor: pointer;
transition: all 0.2s ease;
border-left: 3px solid transparent;
@@ -157,19 +157,19 @@ const goBackToEditor = async () => {
}
.nav-icon {
margin-right: 10px;
font-size: 16px;
margin-right: 8px;
font-size: 14px;
opacity: 0.9;
}
.nav-text {
font-size: 14px;
font-size: 13px;
}
}
}
.settings-footer {
padding: 12px 16px 16px 16px;
padding: 10px 14px 14px 14px;
border-top: 1px solid var(--settings-border);
background-color: var(--settings-card-bg);
@@ -179,7 +179,7 @@ const goBackToEditor = async () => {
gap: 8px;
.section-title {
font-size: 10px;
font-size: 9px;
color: var(--settings-text-secondary);
font-weight: 500;
margin-bottom: 0;
@@ -193,7 +193,7 @@ const goBackToEditor = async () => {
.settings-content {
flex: 1;
height: 100%;
padding: 24px 24px 48px 24px;
padding: 20px 20px 40px 20px;
overflow-y: auto;
background-color: var(--settings-bg);
}

View File

@@ -20,7 +20,7 @@ defineProps<{
<style scoped lang="scss">
.setting-item {
display: flex;
padding: 14px 0;
padding: 12px 0;
border-bottom: 1px solid var(--settings-border);
transition: background-color 0.15s ease;
@@ -35,24 +35,24 @@ defineProps<{
.setting-info {
flex: 1;
padding-right: 20px;
padding-right: 16px;
.setting-title {
font-size: 14px;
font-size: 13px;
font-weight: 500;
margin-bottom: 6px;
margin-bottom: 4px;
color: var(--settings-text);
}
.setting-description {
font-size: 12px;
font-size: 11px;
color: var(--settings-text-secondary);
line-height: 1.5;
line-height: 1.4;
}
}
.setting-control {
width: 200px;
width: 180px;
display: flex;
align-items: center;
justify-content: flex-end;

View File

@@ -15,7 +15,7 @@ defineProps<{
<style scoped lang="scss">
.setting-section {
margin-bottom: 30px;
margin-bottom: 24px;
background-color: var(--settings-card-bg);
border-radius: 6px;
overflow: hidden;
@@ -23,17 +23,17 @@ defineProps<{
border: 1px solid var(--settings-border);
.section-title {
font-size: 14px;
font-size: 13px;
font-weight: 600;
margin: 0;
padding: 12px 16px;
padding: 10px 14px;
background-color: var(--settings-hover);
color: var(--settings-text);
border-bottom: 1px solid var(--settings-border);
}
.section-content {
padding: 8px 16px;
padding: 6px 14px;
}
}
</style>

View File

@@ -1,29 +1,45 @@
<script setup lang="ts">
import { nextTick } from 'vue';
const props = defineProps<{
modelValue: boolean;
disabled?: boolean;
}>();
const emit = defineEmits<{
'update:modelValue': [value: boolean]
}>();
const toggle = () => {
emit('update:modelValue', !props.modelValue);
const toggle = async () => {
if (props.disabled) return;
const newValue = !props.modelValue;
emit('update:modelValue', newValue);
// 确保DOM更新
await nextTick();
};
</script>
<template>
<div class="toggle-switch" :class="{ active: modelValue }" @click="toggle">
<div
class="toggle-switch"
:class="{
active: modelValue,
disabled: disabled
}"
@click="toggle"
>
<div class="toggle-handle"></div>
</div>
</template>
<style scoped lang="scss">
.toggle-switch {
width: 40px;
height: 20px;
width: 36px;
height: 18px;
background-color: var(--settings-input-border);
border-radius: 10px;
border-radius: 9px;
position: relative;
cursor: pointer;
transition: background-color 0.2s;
@@ -33,17 +49,30 @@ const toggle = () => {
background-color: #4a9eff;
.toggle-handle {
transform: translateX(20px);
transform: translateX(18px);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
}
}
&.disabled {
opacity: 0.5;
cursor: not-allowed;
&:hover {
background-color: var(--settings-input-border);
}
&.active:hover {
background-color: #4a9eff;
}
}
.toggle-handle {
position: absolute;
top: 2px;
left: 2px;
width: 16px;
height: 16px;
width: 14px;
height: 14px;
background-color: var(--settings-text);
border-radius: 50%;
transition: all 0.2s ease;

View File

@@ -79,19 +79,19 @@ const updateSystemTheme = async (event: Event) => {
}
.select-input {
min-width: 150px;
padding: 8px 12px;
min-width: 140px;
padding: 6px 10px;
border: 1px solid var(--settings-input-border);
border-radius: 4px;
background-color: var(--settings-input-bg);
color: var(--settings-text);
font-size: 13px;
font-size: 12px;
appearance: none;
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23666666' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");
background-repeat: no-repeat;
background-position: right 8px center;
background-size: 16px;
padding-right: 30px;
background-position: right 6px center;
background-size: 14px;
padding-right: 26px;
transition: border-color 0.2s ease;
&:focus {

View File

@@ -123,7 +123,12 @@ const handleToggleTabType = async () => {
// 创建双向绑定的计算属性
const enableTabIndent = computed({
get: () => configStore.config.editing.enableTabIndent,
set: (value: boolean) => configStore.setEnableTabIndent(value)
set: async (value: boolean) => {
await safeCall(
() => configStore.setEnableTabIndent(value),
'config.tabIndentToggleFailed'
);
}
});
// 保存选项处理器
@@ -138,31 +143,7 @@ const handleAutoSaveDelayChange = async (event: Event) => {
}
};
// 动态字体预览文本
const fontPreviewText = computed(() => {
const currentFont = configStore.config.editing.fontFamily;
// 根据字体类型返回不同的预览文本
if (currentFont.includes('HarmonyOS')) {
return '鸿蒙字体测试';
} else if (currentFont.includes('Microsoft YaHei')) {
return '微软雅黑测试';
} else if (currentFont.includes('PingFang')) {
return '苹方字体测试';
} else if (currentFont.includes('JetBrains')) {
return 'JetBrains Mono';
} else if (currentFont.includes('Fira Code')) {
return 'Fira Code Test';
} else if (currentFont.includes('Source Code')) {
return 'Source Code Pro';
} else if (currentFont.includes('Cascadia')) {
return 'Cascadia Code';
} else if (currentFont.includes('SF Mono') || currentFont.includes('Monaco')) {
return 'System Monospace';
} else {
return 'Font Preview';
}
});
</script>
<template>
@@ -228,20 +209,7 @@ const fontPreviewText = computed(() => {
</div>
</SettingItem>
<div class="font-preview" :style="{
fontSize: `${configStore.config.editing.fontSize}px`,
fontFamily: configStore.config.editing.fontFamily,
fontWeight: configStore.config.editing.fontWeight,
lineHeight: configStore.config.editing.lineHeight
}">
<div class="preview-label">字体预览</div>
<div class="preview-text">
<span>function example() {</span>
<span class="indent">console.log("Hello, 世界!");</span>
<span class="indent">const message = "{{ fontPreviewText }}";</span>
<span>}</span>
</div>
</div>
</SettingSection>
<SettingSection :title="t('settings.tabSettings')">
@@ -314,7 +282,7 @@ const fontPreviewText = computed(() => {
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
font-size: 12px;
transition: all 0.2s ease;
&:hover:not(:disabled) {
@@ -335,7 +303,7 @@ const fontPreviewText = computed(() => {
span {
min-width: 50px;
text-align: center;
font-size: 14px;
font-size: 12px;
color: var(--settings-text);
background-color: var(--settings-input-bg);
border: 1px solid var(--settings-input-border);
@@ -344,65 +312,7 @@ const fontPreviewText = computed(() => {
}
}
.font-size-preview {
margin: 15px 0 5px 20px;
padding: 15px;
background-color: var(--settings-card-bg);
border: 1px solid var(--settings-border);
border-radius: 4px;
font-family: 'Consolas', 'Courier New', monospace;
.preview-label {
font-size: 12px;
color: var(--text-muted);
margin-bottom: 8px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}
.preview-text {
display: flex;
flex-direction: column;
span {
line-height: 1.4;
color: var(--settings-text-secondary);
}
.indent {
padding-left: 20px;
color: #4a9eff;
}
}
}
.font-preview {
margin: 15px 0 5px 20px;
padding: 15px;
background-color: var(--settings-card-bg);
border: 1px solid var(--settings-border);
border-radius: 4px;
.preview-label {
font-size: 12px;
color: var(--text-muted);
margin-bottom: 8px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}
.preview-text {
display: flex;
flex-direction: column;
span {
color: var(--settings-text-secondary);
}
.indent {
padding-left: 20px;
color: #4a9eff;
}
}
}
.font-family-select,
.font-weight-select {
@@ -412,7 +322,7 @@ const fontPreviewText = computed(() => {
border-radius: 4px;
background-color: var(--settings-input-bg);
color: var(--settings-text);
font-size: 13px;
font-size: 12px;
cursor: pointer;
&:focus {
@@ -439,7 +349,7 @@ const fontPreviewText = computed(() => {
border-radius: 4px;
color: var(--settings-text);
cursor: pointer;
font-size: 13px;
font-size: 12px;
text-align: center;
transition: all 0.2s ease;
@@ -460,7 +370,7 @@ const fontPreviewText = computed(() => {
border-radius: 4px;
background-color: var(--settings-input-bg);
color: var(--settings-text);
font-size: 13px;
font-size: 12px;
&:focus {
outline: none;

View File

@@ -472,7 +472,7 @@ onUnmounted(() => {
border: 1px solid var(--settings-input-border);
border-radius: 4px;
color: var(--settings-text-secondary);
font-size: 13px;
font-size: 12px;
transition: all 0.2s ease;
&:hover {
@@ -493,10 +493,10 @@ onUnmounted(() => {
padding: 8px 12px;
background-color: var(--settings-input-bg);
border: 1px solid var(--settings-input-border);
border-radius: 4px;
color: var(--settings-text);
font-size: 13px;
appearance: none;
border-radius: 4px;
color: var(--settings-text);
font-size: 12px;
appearance: none;
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23999999' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");
background-repeat: no-repeat;
background-position: right 8px center;
@@ -530,17 +530,17 @@ onUnmounted(() => {
border-radius: 4px;
margin-top: 8px;
.preview-label {
font-size: 12px;
color: var(--settings-text-secondary);
}
.preview-hotkey {
font-size: 13px;
color: var(--settings-text);
font-weight: 500;
font-family: 'Consolas', 'Courier New', monospace;
}
.preview-label {
font-size: 11px;
color: var(--settings-text-secondary);
}
.preview-hotkey {
font-size: 12px;
color: var(--settings-text);
font-weight: 500;
font-family: 'Consolas', 'Courier New', monospace;
}
}
}
@@ -558,7 +558,7 @@ onUnmounted(() => {
margin-bottom: 12px;
.setting-title {
font-size: 14px;
font-size: 13px;
font-weight: 500;
color: var(--settings-text);
}
@@ -583,7 +583,7 @@ onUnmounted(() => {
border: 1px solid var(--settings-input-border);
border-radius: 4px;
color: var(--settings-text);
font-size: 13px;
font-size: 12px;
line-height: 1.2;
transition: all 0.2s ease;
cursor: pointer;

View File

@@ -132,7 +132,7 @@ const handleKeyDown = (event: KeyboardEvent, binding: KeyBinding) => {
padding: 0 0 10px 0;
border-bottom: 1px solid var(--settings-border);
color: var(--text-muted);
font-size: 13px;
font-size: 12px;
font-weight: 500;
}
@@ -159,7 +159,7 @@ const handleKeyDown = (event: KeyboardEvent, binding: KeyBinding) => {
.command-col {
flex: 1;
padding-right: 10px;
font-size: 14px;
font-size: 13px;
color: var(--settings-text);
}
@@ -176,9 +176,9 @@ const handleKeyDown = (event: KeyboardEvent, binding: KeyBinding) => {
.key-badge {
background-color: var(--settings-input-bg);
padding: 3px 8px;
padding: 2px 6px;
border-radius: 3px;
font-size: 12px;
font-size: 11px;
border: 1px solid var(--settings-input-border);
color: var(--settings-text);
}
@@ -195,7 +195,7 @@ const handleKeyDown = (event: KeyboardEvent, binding: KeyBinding) => {
border-radius: 4px;
color: var(--settings-text);
cursor: pointer;
font-size: 13px;
font-size: 12px;
transition: all 0.2s ease;
&:hover {
@@ -217,6 +217,6 @@ const handleKeyDown = (event: KeyboardEvent, binding: KeyBinding) => {
color: var(--text-muted);
text-align: center;
font-style: italic;
font-size: 14px;
font-size: 13px;
}
</style>

View File

@@ -103,7 +103,7 @@ const downloadUpdate = () => {
margin-bottom: 20px;
.current-version {
font-size: 14px;
font-size: 13px;
.label {
color: var(--text-muted);
@@ -123,7 +123,7 @@ const downloadUpdate = () => {
border-radius: 4px;
color: var(--settings-text);
cursor: pointer;
font-size: 13px;
font-size: 12px;
transition: all 0.2s ease;
display: flex;
align-items: center;
@@ -172,7 +172,7 @@ const downloadUpdate = () => {
margin-bottom: 16px;
.update-title {
font-size: 14px;
font-size: 13px;
font-weight: 500;
color: #4a9eff;
}
@@ -184,7 +184,7 @@ const downloadUpdate = () => {
border-radius: 4px;
color: #ffffff;
cursor: pointer;
font-size: 13px;
font-size: 12px;
transition: all 0.2s ease;
&:hover {
@@ -199,7 +199,7 @@ const downloadUpdate = () => {
.update-notes {
.notes-title {
font-size: 13px;
font-size: 12px;
color: var(--settings-text-secondary);
margin-bottom: 8px;
}
@@ -209,7 +209,7 @@ const downloadUpdate = () => {
padding-left: 20px;
li {
font-size: 13px;
font-size: 12px;
color: var(--settings-text-secondary);
margin-bottom: 6px;