🎨 Change configuration structure

This commit is contained in:
2025-06-03 00:19:16 +08:00
parent 77bd15bed7
commit 81868e8d37
12 changed files with 525 additions and 386 deletions

View File

@@ -10,23 +10,33 @@ import {Create as $Create} from "@wailsio/runtime";
import * as time$0 from "../../../time/models.js";
/**
* AppConfig 应用配置 - 包含业务配置和路径配置
* AppConfig 应用配置 - 按照前端设置页面分类组织
*/
export class AppConfig {
/**
* 编辑器配
* 通用设
*/
"editor": EditorConfig;
"general": GeneralConfig;
/**
* 文档配
* 编辑设
*/
"document": DocumentConfig;
"editing": EditingConfig;
/**
* 路径配
* 外观设
*/
"paths": PathsConfig;
"appearance": AppearanceConfig;
/**
* 快捷键设置
*/
"keyBindings": KeyBindingsConfig;
/**
* 更新设置
*/
"updates": UpdatesConfig;
/**
* 配置元数据
@@ -35,14 +45,20 @@ export class AppConfig {
/** Creates a new AppConfig instance. */
constructor($$source: Partial<AppConfig> = {}) {
if (!("editor" in $$source)) {
this["editor"] = (new EditorConfig());
if (!("general" in $$source)) {
this["general"] = (new GeneralConfig());
}
if (!("document" in $$source)) {
this["document"] = (new DocumentConfig());
if (!("editing" in $$source)) {
this["editing"] = (new EditingConfig());
}
if (!("paths" in $$source)) {
this["paths"] = (new PathsConfig());
if (!("appearance" in $$source)) {
this["appearance"] = (new AppearanceConfig());
}
if (!("keyBindings" in $$source)) {
this["keyBindings"] = (new KeyBindingsConfig());
}
if (!("updates" in $$source)) {
this["updates"] = (new UpdatesConfig());
}
if (!("metadata" in $$source)) {
this["metadata"] = (new ConfigMetadata());
@@ -59,23 +75,58 @@ export class AppConfig {
const $$createField1_0 = $$createType1;
const $$createField2_0 = $$createType2;
const $$createField3_0 = $$createType3;
const $$createField4_0 = $$createType4;
const $$createField5_0 = $$createType5;
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
if ("editor" in $$parsedSource) {
$$parsedSource["editor"] = $$createField0_0($$parsedSource["editor"]);
if ("general" in $$parsedSource) {
$$parsedSource["general"] = $$createField0_0($$parsedSource["general"]);
}
if ("document" in $$parsedSource) {
$$parsedSource["document"] = $$createField1_0($$parsedSource["document"]);
if ("editing" in $$parsedSource) {
$$parsedSource["editing"] = $$createField1_0($$parsedSource["editing"]);
}
if ("paths" in $$parsedSource) {
$$parsedSource["paths"] = $$createField2_0($$parsedSource["paths"]);
if ("appearance" in $$parsedSource) {
$$parsedSource["appearance"] = $$createField2_0($$parsedSource["appearance"]);
}
if ("keyBindings" in $$parsedSource) {
$$parsedSource["keyBindings"] = $$createField3_0($$parsedSource["keyBindings"]);
}
if ("updates" in $$parsedSource) {
$$parsedSource["updates"] = $$createField4_0($$parsedSource["updates"]);
}
if ("metadata" in $$parsedSource) {
$$parsedSource["metadata"] = $$createField3_0($$parsedSource["metadata"]);
$$parsedSource["metadata"] = $$createField5_0($$parsedSource["metadata"]);
}
return new AppConfig($$parsedSource as Partial<AppConfig>);
}
}
/**
* AppearanceConfig 外观设置配置
*/
export class AppearanceConfig {
/**
* 界面语言
*/
"language": LanguageType;
/** Creates a new AppearanceConfig instance. */
constructor($$source: Partial<AppearanceConfig> = {}) {
if (!("language" in $$source)) {
this["language"] = ("" as LanguageType);
}
Object.assign(this, $$source);
}
/**
* Creates a new AppearanceConfig instance from a string or object.
*/
static createFrom($$source: any = {}): AppearanceConfig {
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
return new AppearanceConfig($$parsedSource as Partial<AppearanceConfig>);
}
}
/**
* ConfigMetadata 配置元数据
*/
@@ -141,7 +192,7 @@ export class Document {
* Creates a new Document instance from a string or object.
*/
static createFrom($$source: any = {}): Document {
const $$createField0_0 = $$createType4;
const $$createField0_0 = $$createType6;
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
if ("meta" in $$parsedSource) {
$$parsedSource["meta"] = $$createField0_0($$parsedSource["meta"]);
@@ -150,49 +201,6 @@ export class Document {
}
}
/**
* DocumentConfig 定义文档配置
*/
export class DocumentConfig {
/**
* 自动保存延迟(毫秒)- 内容变更后多久自动保存
*/
"autoSaveDelay": number;
/**
* 变更字符阈值,超过此阈值立即触发保存
*/
"changeThreshold": number;
/**
* 最小保存间隔(毫秒)- 两次保存之间的最小时间间隔避免频繁IO
*/
"minSaveInterval": number;
/** Creates a new DocumentConfig instance. */
constructor($$source: Partial<DocumentConfig> = {}) {
if (!("autoSaveDelay" in $$source)) {
this["autoSaveDelay"] = 0;
}
if (!("changeThreshold" in $$source)) {
this["changeThreshold"] = 0;
}
if (!("minSaveInterval" in $$source)) {
this["minSaveInterval"] = 0;
}
Object.assign(this, $$source);
}
/**
* Creates a new DocumentConfig instance from a string or object.
*/
static createFrom($$source: any = {}): DocumentConfig {
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
return new DocumentConfig($$parsedSource as Partial<DocumentConfig>);
}
}
/**
* DocumentMeta 文档元数据
*/
@@ -245,10 +253,11 @@ export class DocumentMeta {
}
/**
* EditorConfig 定义编辑器配置
* EditingConfig 编辑设置配置
*/
export class EditorConfig {
export class EditingConfig {
/**
* 字体设置
* 字体大小
*/
"fontSize": number;
@@ -269,6 +278,7 @@ export class EditorConfig {
"lineHeight": number;
/**
* Tab设置
* 是否启用Tab缩进
*/
"enableTabIndent": boolean;
@@ -284,17 +294,23 @@ export class EditorConfig {
"tabType": TabType;
/**
* 界面语言
* 保存选项
* 自动保存延迟(毫秒)
*/
"language": LanguageType;
"autoSaveDelay": number;
/**
* 窗口是否置顶
* 变更字符阈值
*/
"alwaysOnTop": boolean;
"changeThreshold": number;
/** Creates a new EditorConfig instance. */
constructor($$source: Partial<EditorConfig> = {}) {
/**
* 最小保存间隔(毫秒)
*/
"minSaveInterval": number;
/** Creates a new EditingConfig instance. */
constructor($$source: Partial<EditingConfig> = {}) {
if (!("fontSize" in $$source)) {
this["fontSize"] = 0;
}
@@ -316,22 +332,80 @@ export class EditorConfig {
if (!("tabType" in $$source)) {
this["tabType"] = ("" as TabType);
}
if (!("language" in $$source)) {
this["language"] = ("" as LanguageType);
if (!("autoSaveDelay" in $$source)) {
this["autoSaveDelay"] = 0;
}
if (!("alwaysOnTop" in $$source)) {
this["alwaysOnTop"] = false;
if (!("changeThreshold" in $$source)) {
this["changeThreshold"] = 0;
}
if (!("minSaveInterval" in $$source)) {
this["minSaveInterval"] = 0;
}
Object.assign(this, $$source);
}
/**
* Creates a new EditorConfig instance from a string or object.
* Creates a new EditingConfig instance from a string or object.
*/
static createFrom($$source: any = {}): EditorConfig {
static createFrom($$source: any = {}): EditingConfig {
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
return new EditorConfig($$parsedSource as Partial<EditorConfig>);
return new EditingConfig($$parsedSource as Partial<EditingConfig>);
}
}
/**
* GeneralConfig 通用设置配置
*/
export class GeneralConfig {
/**
* 窗口是否置顶
*/
"alwaysOnTop": boolean;
/**
* 数据存储路径
*/
"dataPath": string;
/** Creates a new GeneralConfig instance. */
constructor($$source: Partial<GeneralConfig> = {}) {
if (!("alwaysOnTop" in $$source)) {
this["alwaysOnTop"] = false;
}
if (!("dataPath" in $$source)) {
this["dataPath"] = "";
}
Object.assign(this, $$source);
}
/**
* Creates a new GeneralConfig instance from a string or object.
*/
static createFrom($$source: any = {}): GeneralConfig {
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
return new GeneralConfig($$parsedSource as Partial<GeneralConfig>);
}
}
/**
* KeyBindingsConfig 快捷键设置配置
*/
export class KeyBindingsConfig {
/** Creates a new KeyBindingsConfig instance. */
constructor($$source: Partial<KeyBindingsConfig> = {}) {
Object.assign(this, $$source);
}
/**
* Creates a new KeyBindingsConfig instance from a string or object.
*/
static createFrom($$source: any = {}): KeyBindingsConfig {
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
return new KeyBindingsConfig($$parsedSource as Partial<KeyBindingsConfig>);
}
}
@@ -355,33 +429,6 @@ export enum LanguageType {
LangEnUS = "en-US",
};
/**
* PathsConfig 路径配置集合
*/
export class PathsConfig {
/**
* 数据存储路径
*/
"dataPath": string;
/** Creates a new PathsConfig instance. */
constructor($$source: Partial<PathsConfig> = {}) {
if (!("dataPath" in $$source)) {
this["dataPath"] = "";
}
Object.assign(this, $$source);
}
/**
* Creates a new PathsConfig instance from a string or object.
*/
static createFrom($$source: any = {}): PathsConfig {
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
return new PathsConfig($$parsedSource as Partial<PathsConfig>);
}
}
/**
* TabType 定义了制表符类型
*/
@@ -402,9 +449,31 @@ export enum TabType {
TabTypeTab = "tab",
};
/**
* UpdatesConfig 更新设置配置
*/
export class UpdatesConfig {
/** Creates a new UpdatesConfig instance. */
constructor($$source: Partial<UpdatesConfig> = {}) {
Object.assign(this, $$source);
}
/**
* Creates a new UpdatesConfig instance from a string or object.
*/
static createFrom($$source: any = {}): UpdatesConfig {
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
return new UpdatesConfig($$parsedSource as Partial<UpdatesConfig>);
}
}
// Private type creation functions
const $$createType0 = EditorConfig.createFrom;
const $$createType1 = DocumentConfig.createFrom;
const $$createType2 = PathsConfig.createFrom;
const $$createType3 = ConfigMetadata.createFrom;
const $$createType4 = DocumentMeta.createFrom;
const $$createType0 = GeneralConfig.createFrom;
const $$createType1 = EditingConfig.createFrom;
const $$createType2 = AppearanceConfig.createFrom;
const $$createType3 = KeyBindingsConfig.createFrom;
const $$createType4 = UpdatesConfig.createFrom;
const $$createType5 = ConfigMetadata.createFrom;
const $$createType6 = DocumentMeta.createFrom;

View File

@@ -61,7 +61,7 @@ export function GetDiffInfo(oldText: string, newText: string): Promise<$models.D
/**
* GetSaveSettings 获取文档保存设置
*/
export function GetSaveSettings(): Promise<models$0.DocumentConfig | null> & { cancel(): void } {
export function GetSaveSettings(): Promise<models$0.EditingConfig | null> & { cancel(): void } {
let $resultPromise = $Call.ByID(4257471801) as any;
let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType4($result);
@@ -129,7 +129,7 @@ export function UpdateActiveDocumentContent(content: string): Promise<void> & {
/**
* UpdateSaveSettings 更新文档保存设置
*/
export function UpdateSaveSettings(docConfig: models$0.DocumentConfig): Promise<void> & { cancel(): void } {
export function UpdateSaveSettings(docConfig: models$0.EditingConfig): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(1245479534, docConfig) as any;
return $resultPromise;
}
@@ -138,5 +138,5 @@ export function UpdateSaveSettings(docConfig: models$0.DocumentConfig): Promise<
const $$createType0 = models$0.Document.createFrom;
const $$createType1 = $Create.Nullable($$createType0);
const $$createType2 = $models.DiffResult.createFrom;
const $$createType3 = models$0.DocumentConfig.createFrom;
const $$createType3 = models$0.EditingConfig.createFrom;
const $$createType4 = $Create.Nullable($$createType3);

View File

@@ -1,75 +1,79 @@
<script setup lang="ts">
import {useEditorStore} from '@/stores/editorStore';
import {useConfigStore, SUPPORTED_LOCALES, type SupportedLocaleType} from '@/stores/configStore';
import {useLogStore} from '@/stores/logStore';
import { useErrorHandler } from '@/utils/errorHandler';
import { useI18n } from 'vue-i18n';
import { ref, onMounted, watch } from 'vue';
import { useConfigStore } from '@/stores/configStore';
import { useEditorStore } from '@/stores/editorStore';
import { useErrorHandler } from '@/utils/errorHandler';
import * as runtime from '@wailsio/runtime';
import { useRouter } from 'vue-router';
const editorStore = useEditorStore();
const configStore = useConfigStore();
const logStore = useLogStore();
const { safeCall } = useErrorHandler();
const { t, locale } = useI18n();
const router = useRouter();
// 语言下拉菜单
const showLanguageMenu = ref(false);
// 切换语言
const changeLanguage = async (localeCode: SupportedLocaleType) => {
await safeCall(
() => configStore.setLocale(localeCode),
'config.languageChangeFailed'
);
showLanguageMenu.value = false;
};
const supportedLanguages = [
{ code: 'zh-CN', name: '简体中文' },
{ code: 'en-US', name: 'English' }
];
// 切换语言菜单显示
const toggleLanguageMenu = () => {
showLanguageMenu.value = !showLanguageMenu.value;
};
// 窗口置顶控制
const closeLanguageMenu = () => {
showLanguageMenu.value = false;
};
const changeLanguage = async (langCode: string) => {
await safeCall(() => configStore.setLanguage(langCode as any), 'language.changeFailed');
closeLanguageMenu();
};
// 设置窗口置顶
const setWindowAlwaysOnTop = async (isTop: boolean) => {
await safeCall(async () => {
await runtime.Window.SetAlwaysOnTop(isTop);
}, 'window.setTopFailed');
};
// 切换窗口置顶
const toggleAlwaysOnTop = async () => {
await safeCall(async () => {
await configStore.toggleAlwaysOnTop();
// 使用Window.SetAlwaysOnTop方法设置窗口置顶状态
await runtime.Window.SetAlwaysOnTop(configStore.config.alwaysOnTop);
await runtime.Window.SetAlwaysOnTop(configStore.config.general.alwaysOnTop);
}, 'config.alwaysOnTopFailed');
};
// 打开设置页面
const openSettings = () => {
// 跳转到设置页面
const goToSettings = () => {
router.push('/settings');
};
// 设置窗口置顶状态的通用函数
const setWindowAlwaysOnTop = async (alwaysOnTop: boolean) => {
await safeCall(
() => runtime.Window.SetAlwaysOnTop(alwaysOnTop),
'config.alwaysOnTopFailed'
);
};
const isLoaded = ref(false);
// 初始化配置
onMounted(async () => {
// 加载配置
if (!configStore.configLoaded) {
await configStore.initConfig();
}
// 设置窗口置顶状态
if (configStore.config.alwaysOnTop) {
await setWindowAlwaysOnTop(true);
}
onMounted(() => {
isLoaded.value = true;
});
// 监听配置加载完成
watch(() => configStore.configLoaded, (isLoaded) => {
if (isLoaded && configStore.config.alwaysOnTop) {
setWindowAlwaysOnTop(true);
// 监听置顶设置变化
watch(
() => configStore.config.general.alwaysOnTop,
async (newValue) => {
if (!isLoaded.value) return;
await runtime.Window.SetAlwaysOnTop(newValue);
}
);
// 在组件加载完成后应用置顶设置
watch(isLoaded, async (newLoaded) => {
if (newLoaded && configStore.config.general.alwaysOnTop) {
await setWindowAlwaysOnTop(true);
}
});
</script>
@@ -86,34 +90,30 @@ watch(() => configStore.configLoaded, (isLoaded) => {
<span class="stat-item" :title="t('toolbar.editor.selected')" v-if="editorStore.documentStats.selectedCharacters > 0">
{{ t('toolbar.editor.selected') }}: <span class="stat-value">{{ editorStore.documentStats.selectedCharacters }}</span>
</span>
<span v-if="logStore.showLog && logStore.latestLog" class="log-item" :class="'log-' + logStore.latestLog.level"
@click="logStore.hideCurrentLog()">
{{ logStore.latestLog.message }}
</span>
</div>
<div class="actions">
<span class="font-size" :title="t('toolbar.fontSizeTooltip')" @click="() => configStore.resetFontSize()">
{{ configStore.config.fontSize }}px
{{ configStore.config.editing.fontSize }}px
</span>
<span class="tab-settings">
<label :title="t('toolbar.tabLabel')" class="tab-toggle">
<input type="checkbox" :checked="configStore.config.enableTabIndent" @change="() => configStore.toggleTabIndent()"/>
<input type="checkbox" :checked="configStore.config.editing.enableTabIndent" @change="() => configStore.toggleTabIndent()"/>
<span>{{ t('toolbar.tabLabel') }}</span>
</label>
<span class="tab-type" :title="t('toolbar.tabType.' + (configStore.config.tabType === 'spaces' ? 'spaces' : 'tab'))" @click="() => configStore.toggleTabType()">
{{ t('toolbar.tabType.' + (configStore.config.tabType === 'spaces' ? 'spaces' : 'tab')) }}
<span class="tab-type" :title="t('toolbar.tabType.' + (configStore.config.editing.tabType === 'spaces' ? 'spaces' : 'tab'))" @click="() => configStore.toggleTabType()">
{{ t('toolbar.tabType.' + (configStore.config.editing.tabType === 'spaces' ? 'spaces' : 'tab')) }}
</span>
<span class="tab-size" title="Tab大小" v-if="configStore.config.tabType === 'spaces'">
<button class="tab-btn" @click="() => configStore.decreaseTabSize()" :disabled="configStore.config.tabSize <= configStore.tabSize.min">-</button>
<span>{{ configStore.config.tabSize }}</span>
<button class="tab-btn" @click="() => configStore.increaseTabSize()" :disabled="configStore.config.tabSize >= configStore.tabSize.max">+</button>
<span class="tab-size" title="Tab大小" v-if="configStore.config.editing.tabType === 'spaces'">
<button class="tab-btn" @click="() => configStore.decreaseTabSize()" :disabled="configStore.config.editing.tabSize <= configStore.tabSize.min">-</button>
<span>{{ configStore.config.editing.tabSize }}</span>
<button class="tab-btn" @click="() => configStore.increaseTabSize()" :disabled="configStore.config.editing.tabSize >= configStore.tabSize.max">+</button>
</span>
</span>
<!-- 窗口置顶图标按钮 -->
<div
class="pin-button"
:class="{ 'active': configStore.config.alwaysOnTop }"
:class="{ 'active': configStore.config.general.alwaysOnTop }"
:title="t('toolbar.alwaysOnTop')"
@click="toggleAlwaysOnTop"
>
@@ -130,7 +130,7 @@ watch(() => configStore.configLoaded, (isLoaded) => {
</button>
<div class="selector-menu" v-if="showLanguageMenu">
<div
v-for="lang in SUPPORTED_LOCALES"
v-for="lang in supportedLanguages"
:key="lang.code"
class="selector-option"
:class="{ active: locale === lang.code }"
@@ -141,7 +141,7 @@ watch(() => configStore.configLoaded, (isLoaded) => {
</div>
</div>
<button class="settings-btn" :title="t('toolbar.settings')" @click="openSettings">
<button class="settings-btn" :title="t('toolbar.settings')" @click="goToSettings">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="3"></circle>
@@ -177,24 +177,6 @@ watch(() => configStore.configLoaded, (isLoaded) => {
color: #e0e0e0;
}
}
.log-item {
cursor: default;
font-size: 12px;
transition: opacity 0.3s ease;
&.log-info {
color: rgba(177, 176, 176, 0.8);
}
&.log-warning {
color: rgba(240, 230, 140, 0.8);
}
&.log-error {
color: rgba(255, 107, 107, 0.8);
}
}
}
.actions {

View File

@@ -3,7 +3,7 @@ import {computed, reactive} from 'vue';
import {
ConfigService
} from '@/../bindings/voidraft/internal/services';
import {EditorConfig, TabType, LanguageType} from '@/../bindings/voidraft/internal/models/models';
import {AppConfig, GeneralConfig, EditingConfig, AppearanceConfig, TabType, LanguageType} from '@/../bindings/voidraft/internal/models';
import { useI18n } from 'vue-i18n';
import { useErrorHandler } from '@/utils/errorHandler';
import { ConfigUtils } from '@/utils/configUtils';
@@ -24,26 +24,41 @@ export const SUPPORTED_LOCALES = [
] as const;
// 配置键映射和限制的类型定义
type ConfigKeyMap = {
readonly [K in keyof EditorConfig]: string;
type GeneralConfigKeyMap = {
readonly [K in keyof GeneralConfig]: string;
};
type EditingConfigKeyMap = {
readonly [K in keyof EditingConfig]: string;
};
type AppearanceConfigKeyMap = {
readonly [K in keyof AppearanceConfig]: string;
};
type NumberConfigKey = 'fontSize' | 'tabSize' | 'lineHeight';
type StringConfigKey = 'fontFamily' | 'fontWeight';
type BooleanConfigKey = 'enableTabIndent' | 'alwaysOnTop';
type EnumConfigKey = 'tabType' | 'language';
// 配置键映射
const CONFIG_KEY_MAP: ConfigKeyMap = {
fontSize: 'editor.font_size',
fontFamily: 'editor.font_family',
fontWeight: 'editor.font_weight',
lineHeight: 'editor.line_height',
enableTabIndent: 'editor.enable_tab_indent',
tabSize: 'editor.tab_size',
tabType: 'editor.tab_type',
language: 'editor.language',
alwaysOnTop: 'editor.always_on_top'
const GENERAL_CONFIG_KEY_MAP: GeneralConfigKeyMap = {
alwaysOnTop: 'general.always_on_top',
dataPath: 'general.data_path'
} 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',
changeThreshold: 'editing.change_threshold',
minSaveInterval: 'editing.min_save_interval'
} as const;
const APPEARANCE_CONFIG_KEY_MAP: AppearanceConfigKeyMap = {
language: 'appearance.language'
} as const;
// 配置限制
@@ -80,16 +95,32 @@ const getBrowserLanguage = (): SupportedLocaleType => {
};
// 默认配置
const DEFAULT_CONFIG: EditorConfig = {
fontSize: CONFIG_LIMITS.fontSize.default,
fontFamily: FONT_OPTIONS[0].value,
fontWeight: 'normal',
lineHeight: CONFIG_LIMITS.lineHeight.default,
enableTabIndent: true,
tabSize: CONFIG_LIMITS.tabSize.default,
tabType: CONFIG_LIMITS.tabType.default,
language: LanguageType.LangZhCN,
alwaysOnTop: false
const DEFAULT_CONFIG: AppConfig = {
general: {
alwaysOnTop: false,
dataPath: './data'
},
editing: {
fontSize: CONFIG_LIMITS.fontSize.default,
fontFamily: FONT_OPTIONS[0].value,
fontWeight: 'normal',
lineHeight: CONFIG_LIMITS.lineHeight.default,
enableTabIndent: true,
tabSize: CONFIG_LIMITS.tabSize.default,
tabType: CONFIG_LIMITS.tabType.default,
autoSaveDelay: 5000,
changeThreshold: 500,
minSaveInterval: 1000
},
appearance: {
language: LanguageType.LangZhCN
},
keyBindings: {},
updates: {},
metadata: {
version: '1.0.0',
lastUpdated: null
}
};
export const useConfigStore = defineStore('config', () => {
@@ -98,7 +129,7 @@ export const useConfigStore = defineStore('config', () => {
// 响应式状态
const state = reactive({
config: { ...DEFAULT_CONFIG } as EditorConfig,
config: { ...DEFAULT_CONFIG } as AppConfig,
isLoading: false,
configLoaded: false
});
@@ -110,19 +141,49 @@ export const useConfigStore = defineStore('config', () => {
) as Record<NumberConfigKey, ReturnType<typeof createLimitComputed>>;
// 通用配置更新方法
const updateConfig = async <K extends keyof EditorConfig>(key: K, value: EditorConfig[K]): Promise<void> => {
const updateGeneralConfig = async <K extends keyof GeneralConfig>(key: K, value: GeneralConfig[K]): Promise<void> => {
// 确保配置已加载
if (!state.configLoaded && !state.isLoading) {
await initConfig();
}
const backendKey = CONFIG_KEY_MAP[key];
const backendKey = GENERAL_CONFIG_KEY_MAP[key];
if (!backendKey) {
throw new Error(`No backend key mapping found for ${key.toString()}`);
throw new Error(`No backend key mapping found for general.${key.toString()}`);
}
await ConfigService.Set(backendKey, value);
state.config[key] = value;
state.config.general[key] = value;
};
const updateEditingConfig = async <K extends keyof EditingConfig>(key: K, value: EditingConfig[K]): Promise<void> => {
// 确保配置已加载
if (!state.configLoaded && !state.isLoading) {
await initConfig();
}
const backendKey = EDITING_CONFIG_KEY_MAP[key];
if (!backendKey) {
throw new Error(`No backend key mapping found for editing.${key.toString()}`);
}
await ConfigService.Set(backendKey, value);
state.config.editing[key] = value;
};
const updateAppearanceConfig = async <K extends keyof AppearanceConfig>(key: K, value: AppearanceConfig[K]): Promise<void> => {
// 确保配置已加载
if (!state.configLoaded && !state.isLoading) {
await initConfig();
}
const backendKey = APPEARANCE_CONFIG_KEY_MAP[key];
if (!backendKey) {
throw new Error(`No backend key mapping found for appearance.${key.toString()}`);
}
await ConfigService.Set(backendKey, value);
state.config.appearance[key] = value;
};
// 加载配置
@@ -133,8 +194,14 @@ export const useConfigStore = defineStore('config', () => {
try {
const appConfig = await ConfigService.GetConfig();
if (appConfig?.editor) {
Object.assign(state.config, appConfig.editor);
if (appConfig) {
// 合并配置
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);
}
state.configLoaded = true;
@@ -149,23 +216,26 @@ export const useConfigStore = defineStore('config', () => {
const clamp = (value: number) => ConfigUtils.clamp(value, limit.min, limit.max);
return {
increase: () => safeCall(() => updateConfig(key, clamp(state.config[key] + 1)), 'config.saveFailed', 'config.saveSuccess'),
decrease: () => safeCall(() => updateConfig(key, clamp(state.config[key] - 1)), 'config.saveFailed', 'config.saveSuccess'),
set: (value: number) => safeCall(() => updateConfig(key, clamp(value)), 'config.saveFailed', 'config.saveSuccess'),
reset: () => safeCall(() => updateConfig(key, limit.default), 'config.saveFailed', 'config.saveSuccess')
increase: () => safeCall(() => updateEditingConfig(key, clamp(state.config.editing[key] + 1)), 'config.saveFailed', 'config.saveSuccess'),
decrease: () => safeCall(() => updateEditingConfig(key, clamp(state.config.editing[key] - 1)), 'config.saveFailed', 'config.saveSuccess'),
set: (value: number) => safeCall(() => updateEditingConfig(key, clamp(value)), 'config.saveFailed', 'config.saveSuccess'),
reset: () => safeCall(() => updateEditingConfig(key, limit.default), 'config.saveFailed', 'config.saveSuccess')
};
};
// 通用布尔值切换器
const createToggler = <T extends BooleanConfigKey>(key: T) =>
() => safeCall(() => updateConfig(key, !state.config[key]), 'config.saveFailed', 'config.saveSuccess');
const createGeneralToggler = <T extends keyof GeneralConfig>(key: T) =>
() => safeCall(() => updateGeneralConfig(key, !state.config.general[key] as GeneralConfig[T]), 'config.saveFailed', 'config.saveSuccess');
const createEditingToggler = <T extends keyof EditingConfig>(key: T) =>
() => safeCall(() => updateEditingConfig(key, !state.config.editing[key] as EditingConfig[T]), 'config.saveFailed', 'config.saveSuccess');
// 枚举值切换器
const createEnumToggler = <T extends TabType>(key: 'tabType', values: readonly T[]) =>
() => {
const currentIndex = values.indexOf(state.config[key] as T);
const currentIndex = values.indexOf(state.config.editing[key] as T);
const nextIndex = (currentIndex + 1) % values.length;
return safeCall(() => updateConfig(key, values[nextIndex]), 'config.saveFailed', 'config.saveSuccess');
return safeCall(() => updateEditingConfig(key, values[nextIndex]), 'config.saveFailed', 'config.saveSuccess');
};
// 重置配置
@@ -184,8 +254,7 @@ export const useConfigStore = defineStore('config', () => {
// 语言设置方法
const setLanguage = async (language: LanguageType): Promise<void> => {
await safeCall(async () => {
await ConfigService.Set(CONFIG_KEY_MAP.language, language);
state.config.language = language;
await updateAppearanceConfig('language', language);
// 同步更新前端语言
const frontendLocale = ConfigUtils.backendLanguageToFrontend(language);
@@ -193,12 +262,6 @@ export const useConfigStore = defineStore('config', () => {
}, 'config.languageChangeFailed', 'config.languageChanged');
};
// 通过前端语言代码设置语言
const setLocale = async (localeCode: SupportedLocaleType): Promise<void> => {
const backendLanguage = ConfigUtils.frontendLanguageToBackend(localeCode);
await setLanguage(backendLanguage);
};
// 初始化语言设置
const initializeLanguage = async (): Promise<void> => {
try {
@@ -208,7 +271,7 @@ export const useConfigStore = defineStore('config', () => {
}
// 同步前端语言设置
const frontendLocale = ConfigUtils.backendLanguageToFrontend(state.config.language);
const frontendLocale = ConfigUtils.backendLanguageToFrontend(state.config.appearance.language);
locale.value = frontendLocale as any;
} catch (error) {
const browserLang = getBrowserLanguage();
@@ -225,15 +288,16 @@ export const useConfigStore = defineStore('config', () => {
// 创建切换器实例
const togglers = {
tabIndent: createToggler('enableTabIndent'),
alwaysOnTop: createToggler('alwaysOnTop'),
tabIndent: createEditingToggler('enableTabIndent'),
alwaysOnTop: createGeneralToggler('alwaysOnTop'),
tabType: createEnumToggler('tabType', CONFIG_LIMITS.tabType.values)
};
// 字符串配置设置器
const setters = {
fontFamily: (value: string) => safeCall(() => updateConfig('fontFamily', value), 'config.saveFailed', 'config.saveSuccess'),
fontWeight: (value: string) => safeCall(() => updateConfig('fontWeight', value), 'config.saveFailed', 'config.saveSuccess')
fontFamily: (value: string) => safeCall(() => updateEditingConfig('fontFamily', value), 'config.saveFailed', 'config.saveSuccess'),
fontWeight: (value: string) => safeCall(() => updateEditingConfig('fontWeight', value), 'config.saveFailed', 'config.saveSuccess'),
dataPath: (value: string) => safeCall(() => updateGeneralConfig('dataPath', value), 'config.saveFailed', 'config.saveSuccess')
};
return {
@@ -248,11 +312,9 @@ export const useConfigStore = defineStore('config', () => {
// 核心方法
initConfig: () => safeCall(() => initConfig(), 'config.loadFailed', 'config.loadSuccess'),
resetConfig,
updateConfig: (key: keyof EditorConfig, value: any) => safeCall(() => updateConfig(key, value), 'config.saveFailed', 'config.saveSuccess'),
// 语言相关方法
setLanguage,
setLocale,
initializeLanguage,
// 字体大小操作
@@ -279,6 +341,9 @@ export const useConfigStore = defineStore('config', () => {
// 字体操作
setFontFamily: setters.fontFamily,
setFontWeight: setters.fontWeight,
// 路径操作
setDataPath: setters.dataPath,
};
},{
persist: true,

View File

@@ -33,7 +33,7 @@ export const useEditorStore = defineStore('editor', () => {
// 更新编辑器的字体大小
const editorDOM = editorView.value.dom;
if (editorDOM) {
editorDOM.style.fontSize = `${configStore.config.fontSize}px`;
editorDOM.style.fontSize = `${configStore.config.editing.fontSize}px`;
editorView.value?.requestMeasure();
}
}

View File

@@ -53,17 +53,17 @@ const createEditor = async () => {
// 获取Tab相关扩展
const tabExtensions = getTabExtensions(
configStore.config.tabSize,
configStore.config.enableTabIndent,
configStore.config.tabType
configStore.config.editing.tabSize,
configStore.config.editing.enableTabIndent,
configStore.config.editing.tabType
);
// 创建字体扩展
const fontExtension = createFontExtensionFromBackend({
fontFamily: configStore.config.fontFamily,
fontSize: configStore.config.fontSize,
lineHeight: configStore.config.lineHeight,
fontWeight: configStore.config.fontWeight
fontFamily: configStore.config.editing.fontFamily,
fontSize: configStore.config.editing.fontSize,
lineHeight: configStore.config.editing.lineHeight,
fontWeight: configStore.config.editing.fontWeight
});
// 创建统计信息更新扩展
@@ -148,9 +148,9 @@ const reconfigureTabSettings = () => {
if (!editorStore.editorView) return;
updateTabConfig(
editorStore.editorView as EditorView,
configStore.config.tabSize,
configStore.config.enableTabIndent,
configStore.config.tabType
configStore.config.editing.tabSize,
configStore.config.editing.enableTabIndent,
configStore.config.editing.tabType
);
};
@@ -158,33 +158,33 @@ const reconfigureTabSettings = () => {
const reconfigureFontSettings = () => {
if (!editorStore.editorView) return;
updateFontConfig(editorStore.editorView as EditorView, {
fontFamily: configStore.config.fontFamily,
fontSize: configStore.config.fontSize,
lineHeight: configStore.config.lineHeight,
fontWeight: configStore.config.fontWeight
fontFamily: configStore.config.editing.fontFamily,
fontSize: configStore.config.editing.fontSize,
lineHeight: configStore.config.editing.lineHeight,
fontWeight: configStore.config.editing.fontWeight
});
};
// 监听Tab设置变化
watch(() => configStore.config.tabSize, reconfigureTabSettings);
watch(() => configStore.config.enableTabIndent, reconfigureTabSettings);
watch(() => configStore.config.tabType, reconfigureTabSettings);
watch([
() => configStore.config.editing.tabSize,
() => configStore.config.editing.enableTabIndent,
() => configStore.config.editing.tabType,
], () => {
reconfigureTabSettings();
});
// 监听字体大小变化
watch(() => configStore.config.fontSize, () => {
watch([
() => configStore.config.editing.fontFamily,
() => configStore.config.editing.fontSize,
() => configStore.config.editing.lineHeight,
() => configStore.config.editing.fontWeight,
], () => {
reconfigureFontSettings();
editorStore.applyFontSize();
});
// 监听字体族变化
watch(() => configStore.config.fontFamily, reconfigureFontSettings);
// 监听字体粗细变化
watch(() => configStore.config.fontWeight, reconfigureFontSettings);
// 监听行高变化
watch(() => configStore.config.lineHeight, reconfigureFontSettings);
onMounted(() => {
// 创建编辑器
createEditor();

View File

@@ -47,7 +47,7 @@ const selectTheme = (themeId: string) => {
<div class="settings-page">
<SettingSection :title="t('settings.language')">
<SettingItem :title="t('settings.language')" :description="t('settings.restartRequired')">
<select class="select-input" :value="configStore.config.language" @change="updateLanguage">
<select class="select-input" :value="configStore.config.appearance.language" @change="updateLanguage">
<option v-for="option in languageOptions" :key="option.value" :value="option.value">
{{ option.label }}
</option>

View File

@@ -22,7 +22,7 @@ onMounted(async () => {
// 字体选择选项
const fontFamilyOptions = FONT_OPTIONS;
const currentFontFamily = computed(() => configStore.config.fontFamily);
const currentFontFamily = computed(() => configStore.config.editing.fontFamily);
// 字体选择
const handleFontFamilyChange = async (event: Event) => {
@@ -60,7 +60,7 @@ const handleFontWeightChange = async (event: Event) => {
// 行高控制
const increaseLineHeight = async () => {
const newLineHeight = Math.min(3.0, configStore.config.lineHeight + 0.1);
const newLineHeight = Math.min(3.0, configStore.config.editing.lineHeight + 0.1);
await safeCall(
() => configStore.setLineHeight(Math.round(newLineHeight * 10) / 10),
'config.lineHeightIncreaseFailed'
@@ -68,7 +68,7 @@ const increaseLineHeight = async () => {
};
const decreaseLineHeight = async () => {
const newLineHeight = Math.max(1.0, configStore.config.lineHeight - 0.1);
const newLineHeight = Math.max(1.0, configStore.config.editing.lineHeight - 0.1);
await safeCall(
() => configStore.setLineHeight(Math.round(newLineHeight * 10) / 10),
'config.lineHeightDecreaseFailed'
@@ -92,7 +92,7 @@ const decreaseFontSize = async () => {
// Tab类型切换
const tabTypeText = computed(() => {
return configStore.config.tabType === TabType.TabTypeSpaces
return configStore.config.editing.tabType === TabType.TabTypeSpaces
? t('settings.spaces')
: t('settings.tabs');
});
@@ -156,7 +156,7 @@ const handleToggleTabType = async () => {
>
<div class="number-control">
<button @click="decreaseFontSize" class="control-button">-</button>
<span>{{ configStore.config.fontSize }}px</span>
<span>{{ configStore.config.editing.fontSize }}px</span>
<button @click="increaseFontSize" class="control-button">+</button>
</div>
</SettingItem>
@@ -167,7 +167,7 @@ const handleToggleTabType = async () => {
>
<select
class="font-weight-select"
:value="configStore.config.fontWeight"
:value="configStore.config.editing.fontWeight"
@change="handleFontWeightChange"
>
<option
@@ -186,16 +186,16 @@ const handleToggleTabType = async () => {
>
<div class="number-control">
<button @click="decreaseLineHeight" class="control-button">-</button>
<span>{{ configStore.config.lineHeight.toFixed(1) }}</span>
<span>{{ configStore.config.editing.lineHeight.toFixed(1) }}</span>
<button @click="increaseLineHeight" class="control-button">+</button>
</div>
</SettingItem>
<div class="font-preview" :style="{
fontSize: `${configStore.config.fontSize}px`,
fontFamily: configStore.config.fontFamily,
fontWeight: configStore.config.fontWeight,
lineHeight: configStore.config.lineHeight
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">
@@ -210,9 +210,9 @@ const handleToggleTabType = async () => {
<SettingSection :title="t('settings.tabSettings')">
<SettingItem :title="t('settings.tabSize')">
<div class="number-control">
<button @click="decreaseTabSize" class="control-button" :disabled="configStore.config.tabSize <= configStore.tabSize.min">-</button>
<span>{{ configStore.config.tabSize }}</span>
<button @click="increaseTabSize" class="control-button" :disabled="configStore.config.tabSize >= configStore.tabSize.max">+</button>
<button @click="decreaseTabSize" class="control-button" :disabled="configStore.config.editing.tabSize <= configStore.tabSize.min">-</button>
<span>{{ configStore.config.editing.tabSize }}</span>
<button @click="increaseTabSize" class="control-button" :disabled="configStore.config.editing.tabSize >= configStore.tabSize.max">+</button>
</div>
</SettingItem>
@@ -224,7 +224,7 @@ const handleToggleTabType = async () => {
<SettingItem :title="t('settings.enableTabIndent')">
<ToggleSwitch
v-model="configStore.config.enableTabIndent"
v-model="configStore.config.editing.enableTabIndent"
@update:modelValue="handleToggleTabIndent"
/>
</SettingItem>

View File

@@ -52,7 +52,7 @@ const resetSettings = async () => {
<div class="settings-page">
<SettingSection :title="t('settings.globalHotkey')">
<SettingItem :title="t('settings.enableGlobalHotkey')">
<ToggleSwitch v-model="configStore.config.alwaysOnTop" /> <!-- 此处使用alwaysOnTop作为示例 -->
<ToggleSwitch v-model="configStore.config.general.alwaysOnTop" /> <!-- 此处使用alwaysOnTop作为示例 -->
</SettingItem>
<div class="hotkey-selector">
@@ -87,19 +87,19 @@ const resetSettings = async () => {
<SettingSection :title="t('settings.window')">
<SettingItem :title="t('settings.showInSystemTray')">
<ToggleSwitch v-model="configStore.config.alwaysOnTop" /> <!-- 需要后端实现 -->
<ToggleSwitch v-model="configStore.config.general.alwaysOnTop" /> <!-- 需要后端实现 -->
</SettingItem>
<SettingItem :title="t('settings.alwaysOnTop')">
<ToggleSwitch v-model="configStore.config.alwaysOnTop" @update:modelValue="configStore.toggleAlwaysOnTop" />
<ToggleSwitch v-model="configStore.config.general.alwaysOnTop" @update:modelValue="configStore.toggleAlwaysOnTop" />
</SettingItem>
</SettingSection>
<SettingSection :title="t('settings.bufferFiles')">
<SettingItem :title="t('settings.useCustomLocation')">
<ToggleSwitch v-model="configStore.config.alwaysOnTop" /> <!-- 需要后端实现 -->
<ToggleSwitch v-model="configStore.config.general.alwaysOnTop" /> <!-- 需要后端实现 -->
</SettingItem>
<div class="directory-selector">
<div class="path-display">{{ configStore.config.alwaysOnTop ? 'C:/Custom/Path' : 'Default Location' }}</div>
<div class="path-display">{{ configStore.config.general.alwaysOnTop ? 'C:/Custom/Path' : 'Default Location' }}</div>
<button class="select-button">{{ t('settings.selectDirectory') }}</button>
</div>
</SettingSection>