🎨 Refactor config service

This commit is contained in:
2025-05-01 00:51:16 +08:00
parent 198ba44ceb
commit c9aa8aebcb
16 changed files with 630 additions and 471 deletions

View File

@@ -10,7 +10,7 @@ import {Create as $Create} from "@wailsio/runtime";
import * as time$0 from "../../../time/models.js"; import * as time$0 from "../../../time/models.js";
/** /**
* AppConfig 应用配置 * AppConfig 应用配置 - 包含业务配置和路径配置
*/ */
export class AppConfig { export class AppConfig {
/** /**
@@ -21,7 +21,7 @@ export class AppConfig {
/** /**
* 路径配置 * 路径配置
*/ */
"paths": PathConfig; "paths": PathsConfig;
/** /**
* 配置元数据 * 配置元数据
@@ -34,7 +34,7 @@ export class AppConfig {
this["editor"] = (new EditorConfig()); this["editor"] = (new EditorConfig());
} }
if (!("paths" in $$source)) { if (!("paths" in $$source)) {
this["paths"] = (new PathConfig()); this["paths"] = (new PathsConfig());
} }
if (!("metadata" in $$source)) { if (!("metadata" in $$source)) {
this["metadata"] = (new ConfigMetadata()); this["metadata"] = (new ConfigMetadata());
@@ -108,11 +108,6 @@ export class EditorConfig {
*/ */
"fontSize": number; "fontSize": number;
/**
* 文件保存的编码
*/
"encoding": EncodingType;
/** /**
* 是否启用Tab缩进 * 是否启用Tab缩进
*/ */
@@ -138,9 +133,6 @@ export class EditorConfig {
if (!("fontSize" in $$source)) { if (!("fontSize" in $$source)) {
this["fontSize"] = 0; this["fontSize"] = 0;
} }
if (!("encoding" in $$source)) {
this["encoding"] = ("" as EncodingType);
}
if (!("enableTabIndent" in $$source)) { if (!("enableTabIndent" in $$source)) {
this["enableTabIndent"] = false; this["enableTabIndent"] = false;
} }
@@ -166,56 +158,6 @@ export class EditorConfig {
} }
} }
/**
* EncodingType 定义文件编码格式类型
*/
export enum EncodingType {
/**
* The Go zero value for the underlying type of the enum.
*/
$zero = "",
/**
* EncodingUTF8 UTF-8编码
*/
EncodingUTF8 = "UTF-8",
/**
* EncodingUTF8BOM UTF-8带BOM编码
*/
EncodingUTF8BOM = "UTF-8-BOM",
/**
* EncodingUTF16LE UTF-16小端编码
*/
EncodingUTF16LE = "UTF-16 LE",
/**
* EncodingUTF16BE UTF-16大端编码
*/
EncodingUTF16BE = "UTF-16 BE",
/**
* EncodingISO88591 ISO-8859-1编码
*/
EncodingISO88591 = "ISO-8859-1",
/**
* EncodingGB18030 GB18030编码
*/
EncodingGB18030 = "GB18030",
/**
* EncodingGBK GBK编码
*/
EncodingGBK = "GBK",
/**
* EncodingBig5 Big5编码
*/
EncodingBig5 = "Big5",
};
/** /**
* LanguageType 语言类型定义 * LanguageType 语言类型定义
*/ */
@@ -237,37 +179,45 @@ export enum LanguageType {
}; };
/** /**
* PathConfig 定义配置文件路径相关配置 * PathsConfig 路径配置集合
*/ */
export class PathConfig { export class PathsConfig {
/**
* 根目录
*/
"rootDir": string;
/** /**
* 配置文件路径 * 配置文件路径
*/ */
"configPath": string; "configPath": string;
/** Creates a new PathConfig instance. */ /**
constructor($$source: Partial<PathConfig> = {}) { * 日志文件路径
if (!("rootDir" in $$source)) { */
this["rootDir"] = ""; "logPath": string;
}
/**
* 数据存储路径
*/
"dataPath": string;
/** Creates a new PathsConfig instance. */
constructor($$source: Partial<PathsConfig> = {}) {
if (!("configPath" in $$source)) { if (!("configPath" in $$source)) {
this["configPath"] = ""; this["configPath"] = "";
} }
if (!("logPath" in $$source)) {
this["logPath"] = "";
}
if (!("dataPath" in $$source)) {
this["dataPath"] = "";
}
Object.assign(this, $$source); Object.assign(this, $$source);
} }
/** /**
* Creates a new PathConfig instance from a string or object. * Creates a new PathsConfig instance from a string or object.
*/ */
static createFrom($$source: any = {}): PathConfig { static createFrom($$source: any = {}): PathsConfig {
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source; let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
return new PathConfig($$parsedSource as Partial<PathConfig>); return new PathsConfig($$parsedSource as Partial<PathsConfig>);
} }
} }
@@ -293,5 +243,5 @@ export enum TabType {
// Private type creation functions // Private type creation functions
const $$createType0 = EditorConfig.createFrom; const $$createType0 = EditorConfig.createFrom;
const $$createType1 = PathConfig.createFrom; const $$createType1 = PathsConfig.createFrom;
const $$createType2 = ConfigMetadata.createFrom; const $$createType2 = ConfigMetadata.createFrom;

View File

@@ -12,13 +12,13 @@ import {Call as $Call, Create as $Create} from "@wailsio/runtime";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports // @ts-ignore: Unused imports
import * as models$0 from "../models/models.js"; import * as models$0 from "../../models/models.js";
/** /**
* GetConfig * GetConfig
*/ */
export function GetConfig(): Promise<models$0.AppConfig | null> & { cancel(): void } { export function GetConfig(): Promise<models$0.AppConfig | null> & { cancel(): void } {
let $resultPromise = $Call.ByID(1013336538) as any; let $resultPromise = $Call.ByID(3332910023) as any;
let $typingPromise = $resultPromise.then(($result: any) => { let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType1($result); return $$createType1($result);
}) as any; }) as any;
@@ -26,11 +26,19 @@ export function GetConfig(): Promise<models$0.AppConfig | null> & { cancel(): vo
return $typingPromise; return $typingPromise;
} }
/**
* GetConfigPath
*/
export function GetConfigPath(): Promise<string> & { cancel(): void } {
let $resultPromise = $Call.ByID(2145796624) as any;
return $resultPromise;
}
/** /**
* GetEditorConfig * GetEditorConfig
*/ */
export function GetEditorConfig(): Promise<models$0.EditorConfig> & { cancel(): void } { export function GetEditorConfig(): Promise<models$0.EditorConfig> & { cancel(): void } {
let $resultPromise = $Call.ByID(3648153351) as any; let $resultPromise = $Call.ByID(3738882774) as any;
let $typingPromise = $resultPromise.then(($result: any) => { let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType2($result); return $$createType2($result);
}) as any; }) as any;
@@ -42,7 +50,7 @@ export function GetEditorConfig(): Promise<models$0.EditorConfig> & { cancel():
* GetLanguage * GetLanguage
*/ */
export function GetLanguage(): Promise<models$0.LanguageType> & { cancel(): void } { export function GetLanguage(): Promise<models$0.LanguageType> & { cancel(): void } {
let $resultPromise = $Call.ByID(3409375894) as any; let $resultPromise = $Call.ByID(1762264443) as any;
return $resultPromise; return $resultPromise;
} }
@@ -50,7 +58,7 @@ export function GetLanguage(): Promise<models$0.LanguageType> & { cancel(): void
* GetMetadata * GetMetadata
*/ */
export function GetMetadata(): Promise<models$0.ConfigMetadata> & { cancel(): void } { export function GetMetadata(): Promise<models$0.ConfigMetadata> & { cancel(): void } {
let $resultPromise = $Call.ByID(3276720617) as any; let $resultPromise = $Call.ByID(356327488) as any;
let $typingPromise = $resultPromise.then(($result: any) => { let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType3($result); return $$createType3($result);
}) as any; }) as any;
@@ -59,10 +67,10 @@ export function GetMetadata(): Promise<models$0.ConfigMetadata> & { cancel(): vo
} }
/** /**
* GetPathConfig * GetPaths
*/ */
export function GetPathConfig(): Promise<models$0.PathConfig> & { cancel(): void } { export function GetPaths(): Promise<models$0.PathsConfig> & { cancel(): void } {
let $resultPromise = $Call.ByID(2053285689) as any; let $resultPromise = $Call.ByID(1115810463) as any;
let $typingPromise = $resultPromise.then(($result: any) => { let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType4($result); return $$createType4($result);
}) as any; }) as any;
@@ -74,7 +82,7 @@ export function GetPathConfig(): Promise<models$0.PathConfig> & { cancel(): void
* ResetConfig * ResetConfig
*/ */
export function ResetConfig(): Promise<void> & { cancel(): void } { export function ResetConfig(): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(3593047389) as any; let $resultPromise = $Call.ByID(2265390548) as any;
return $resultPromise; return $resultPromise;
} }
@@ -82,7 +90,7 @@ export function ResetConfig(): Promise<void> & { cancel(): void } {
* SaveConfig * SaveConfig
*/ */
export function SaveConfig(config: models$0.AppConfig | null): Promise<void> & { cancel(): void } { export function SaveConfig(config: models$0.AppConfig | null): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(616684383, config) as any; let $resultPromise = $Call.ByID(2710445504, config) as any;
return $resultPromise; return $resultPromise;
} }
@@ -90,7 +98,15 @@ export function SaveConfig(config: models$0.AppConfig | null): Promise<void> & {
* SetLanguage * SetLanguage
*/ */
export function SetLanguage(language: models$0.LanguageType): Promise<void> & { cancel(): void } { export function SetLanguage(language: models$0.LanguageType): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(814725002, language) as any; let $resultPromise = $Call.ByID(2553541807, language) as any;
return $resultPromise;
}
/**
* UpdateConfigPath
*/
export function UpdateConfigPath(newPath: string): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(251712339, newPath) as any;
return $resultPromise; return $resultPromise;
} }
@@ -98,7 +114,7 @@ export function SetLanguage(language: models$0.LanguageType): Promise<void> & {
* UpdateEditorConfig * UpdateEditorConfig
*/ */
export function UpdateEditorConfig(editorConfig: models$0.EditorConfig): Promise<void> & { cancel(): void } { export function UpdateEditorConfig(editorConfig: models$0.EditorConfig): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(1237949666, editorConfig) as any; let $resultPromise = $Call.ByID(420530601, editorConfig) as any;
return $resultPromise; return $resultPromise;
} }
@@ -106,15 +122,15 @@ export function UpdateEditorConfig(editorConfig: models$0.EditorConfig): Promise
* UpdateMetadata * UpdateMetadata
*/ */
export function UpdateMetadata(metadata: models$0.ConfigMetadata): Promise<void> & { cancel(): void } { export function UpdateMetadata(metadata: models$0.ConfigMetadata): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(3353893284, metadata) as any; let $resultPromise = $Call.ByID(1687760751, metadata) as any;
return $resultPromise; return $resultPromise;
} }
/** /**
* UpdatePathConfig * UpdatePaths
*/ */
export function UpdatePathConfig(pathConfig: models$0.PathConfig): Promise<void> & { cancel(): void } { export function UpdatePaths(paths: models$0.PathsConfig): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(1492772004, pathConfig) as any; let $resultPromise = $Call.ByID(3466187518, paths) as any;
return $resultPromise; return $resultPromise;
} }
@@ -123,4 +139,4 @@ const $$createType0 = models$0.AppConfig.createFrom;
const $$createType1 = $Create.Nullable($$createType0); const $$createType1 = $Create.Nullable($$createType0);
const $$createType2 = models$0.EditorConfig.createFrom; const $$createType2 = models$0.EditorConfig.createFrom;
const $$createType3 = models$0.ConfigMetadata.createFrom; const $$createType3 = models$0.ConfigMetadata.createFrom;
const $$createType4 = models$0.PathConfig.createFrom; const $$createType4 = models$0.PathsConfig.createFrom;

View File

@@ -5,7 +5,6 @@ import {useLogStore} from '@/stores/logStore';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { ref } from 'vue'; import { ref } from 'vue';
import {SUPPORTED_LOCALES, setLocale, SupportedLocaleType} from '@/i18n'; import {SUPPORTED_LOCALES, setLocale, SupportedLocaleType} from '@/i18n';
import { EncodingType } from '@/../bindings/voidraft/internal/models/models';
const editorStore = useEditorStore(); const editorStore = useEditorStore();
const configStore = useConfigStore(); const configStore = useConfigStore();
@@ -14,20 +13,6 @@ const { t, locale } = useI18n();
// 语言下拉菜单 // 语言下拉菜单
const showLanguageMenu = ref(false); const showLanguageMenu = ref(false);
// 编码下拉菜单
const showEncodingMenu = ref(false);
// 支持的编码格式
const SUPPORTED_ENCODINGS = [
{ code: EncodingType.EncodingUTF8, name: 'UTF-8' },
{ code: EncodingType.EncodingUTF8BOM, name: 'UTF-8 with BOM' },
{ code: EncodingType.EncodingUTF16LE, name: 'UTF-16 LE' },
{ code: EncodingType.EncodingUTF16BE, name: 'UTF-16 BE' },
{ code: EncodingType.EncodingISO88591, name: 'ISO-8859-1' },
{ code: EncodingType.EncodingGB18030, name: 'GB18030' },
{ code: EncodingType.EncodingGBK, name: 'GBK' },
{ code: EncodingType.EncodingBig5, name: 'Big5' }
];
// 切换语言 // 切换语言
const changeLanguage = (localeCode: SupportedLocaleType) => { const changeLanguage = (localeCode: SupportedLocaleType) => {
@@ -38,30 +23,10 @@ const changeLanguage = (localeCode: SupportedLocaleType) => {
// 切换语言菜单显示 // 切换语言菜单显示
const toggleLanguageMenu = () => { const toggleLanguageMenu = () => {
showLanguageMenu.value = !showLanguageMenu.value; showLanguageMenu.value = !showLanguageMenu.value;
if (showLanguageMenu.value) {
showEncodingMenu.value = false;
}
}; };
// 切换编码
const changeEncoding = (encoding: EncodingType) => {
configStore.setEncoding(encoding);
showEncodingMenu.value = false;
};
// 切换编码菜单显示
const toggleEncodingMenu = () => {
showEncodingMenu.value = !showEncodingMenu.value;
if (showEncodingMenu.value) {
showLanguageMenu.value = false;
}
};
// 获取编码名称
const getEncodingDisplayName = (encoding: EncodingType) => {
const encodingItem = SUPPORTED_ENCODINGS.find(item => item.code === encoding);
return encodingItem ? encodingItem.name : encoding;
};
</script> </script>
<template> <template>
@@ -100,25 +65,6 @@ const getEncodingDisplayName = (encoding: EncodingType) => {
</span> </span>
</span> </span>
<!-- 编码选择按钮 -->
<div class="selector-dropdown">
<button class="selector-btn" @click="toggleEncodingMenu">
{{ getEncodingDisplayName(configStore.config.encoding) }}
<span class="arrow"></span>
</button>
<div class="selector-menu" v-if="showEncodingMenu">
<div
v-for="encoding in SUPPORTED_ENCODINGS"
:key="encoding.code"
class="selector-option"
:class="{ active: configStore.config.encoding === encoding.code }"
@click="changeEncoding(encoding.code)"
>
{{ encoding.name }}
</div>
</div>
</div>
<!-- 语言切换按钮 --> <!-- 语言切换按钮 -->
<div class="selector-dropdown"> <div class="selector-dropdown">
<button class="selector-btn" @click="toggleLanguageMenu"> <button class="selector-btn" @click="toggleLanguageMenu">

View File

@@ -1,6 +1,6 @@
import {createI18n} from 'vue-i18n'; import {createI18n} from 'vue-i18n';
import messages from './locales'; import messages from './locales';
import { GetLanguage, SetLanguage } from '@/../bindings/voidraft/internal/services/configservice'; import { ConfigService } from '@/../bindings/voidraft/internal/services/config';
import { LanguageType } from '@/../bindings/voidraft/internal/models'; import { LanguageType } from '@/../bindings/voidraft/internal/models';
// 定义支持的语言类型 // 定义支持的语言类型
@@ -40,7 +40,7 @@ const i18n = createI18n({
}); });
// 立即从后端获取语言设置 // 立即从后端获取语言设置
GetLanguage().then(lang => { ConfigService.GetLanguage().then(lang => {
if (lang) { if (lang) {
i18n.global.locale = lang as any; i18n.global.locale = lang as any;
} }
@@ -55,7 +55,7 @@ GetLanguage().then(lang => {
export const setLocale = (locale: SupportedLocaleType) => { export const setLocale = (locale: SupportedLocaleType) => {
if (SUPPORTED_LOCALES.some(l => l.code === locale)) { if (SUPPORTED_LOCALES.some(l => l.code === locale)) {
// 更新后端配置 // 更新后端配置
SetLanguage(locale as LanguageType) ConfigService.SetLanguage(locale as LanguageType)
.then(() => { .then(() => {
i18n.global.locale = locale; i18n.global.locale = locale;
document.documentElement.setAttribute('lang', locale); document.documentElement.setAttribute('lang', locale);

View File

@@ -2,11 +2,9 @@ import {defineStore} from 'pinia';
import {ref, watch} from 'vue'; import {ref, watch} from 'vue';
import {useDebounceFn} from '@vueuse/core'; import {useDebounceFn} from '@vueuse/core';
import { import {
GetEditorConfig, ConfigService
ResetConfig, } from '@/../bindings/voidraft/internal/services/config';
UpdateEditorConfig import {EditorConfig, TabType} from '@/../bindings/voidraft/internal/models/models';
} from '@/../bindings/voidraft/internal/services/configservice';
import {EditorConfig, TabType, EncodingType} from '@/../bindings/voidraft/internal/models/models';
import {useLogStore} from './logStore'; import {useLogStore} from './logStore';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
@@ -20,24 +18,11 @@ const DEFAULT_TAB_SIZE = 4;
const MIN_TAB_SIZE = 2; const MIN_TAB_SIZE = 2;
const MAX_TAB_SIZE = 8; const MAX_TAB_SIZE = 8;
// 支持的编码
const SUPPORTED_ENCODINGS = [
EncodingType.EncodingUTF8,
EncodingType.EncodingUTF8BOM,
EncodingType.EncodingUTF16LE,
EncodingType.EncodingUTF16BE,
EncodingType.EncodingISO88591,
EncodingType.EncodingGB18030,
EncodingType.EncodingGBK,
EncodingType.EncodingBig5
];
// 配置项限制定义 // 配置项限制定义
const CONFIG_LIMITS = { const CONFIG_LIMITS = {
fontSize: { min: MIN_FONT_SIZE, max: MAX_FONT_SIZE, default: DEFAULT_FONT_SIZE }, fontSize: { min: MIN_FONT_SIZE, max: MAX_FONT_SIZE, default: DEFAULT_FONT_SIZE },
tabSize: { min: MIN_TAB_SIZE, max: MAX_TAB_SIZE, default: DEFAULT_TAB_SIZE }, tabSize: { min: MIN_TAB_SIZE, max: MAX_TAB_SIZE, default: DEFAULT_TAB_SIZE },
tabType: { values: [TabType.TabTypeSpaces, TabType.TabTypeTab], default: TabType.TabTypeSpaces }, tabType: { values: [TabType.TabTypeSpaces, TabType.TabTypeTab], default: TabType.TabTypeSpaces },
encoding: { values: SUPPORTED_ENCODINGS, default: EncodingType.EncodingUTF8 }
}; };
export const useConfigStore = defineStore('config', () => { export const useConfigStore = defineStore('config', () => {
@@ -48,7 +33,6 @@ export const useConfigStore = defineStore('config', () => {
// 配置状态 // 配置状态
const config = ref<EditorConfig>(new EditorConfig({ const config = ref<EditorConfig>(new EditorConfig({
fontSize: DEFAULT_FONT_SIZE, fontSize: DEFAULT_FONT_SIZE,
encoding: EncodingType.EncodingUTF8,
enableTabIndent: true, enableTabIndent: true,
tabSize: DEFAULT_TAB_SIZE, tabSize: DEFAULT_TAB_SIZE,
tabType: TabType.TabTypeSpaces tabType: TabType.TabTypeSpaces
@@ -60,7 +44,7 @@ export const useConfigStore = defineStore('config', () => {
// 从后端加载配置 // 从后端加载配置
async function loadConfigFromBackend() { async function loadConfigFromBackend() {
try { try {
config.value = await GetEditorConfig(); config.value = await ConfigService.GetEditorConfig();
// 验证并纠正配置 // 验证并纠正配置
validateAndFixConfig(); validateAndFixConfig();
@@ -110,15 +94,6 @@ export const useConfigStore = defineStore('config', () => {
hasChanges = true; hasChanges = true;
} }
// 验证编码类型是否合法
if (!CONFIG_LIMITS.encoding.values.includes(config.value.encoding)) {
const oldValue = config.value.encoding;
config.value.encoding = CONFIG_LIMITS.encoding.default;
logStore.warning(t('config.encodingFixed'));
hasChanges = true;
}
// 如果配置被修正,保存回后端 // 如果配置被修正,保存回后端
if (hasChanges && configLoaded.value) { if (hasChanges && configLoaded.value) {
saveConfigToBackend(); saveConfigToBackend();
@@ -128,7 +103,7 @@ export const useConfigStore = defineStore('config', () => {
// 使用防抖保存配置到后端 // 使用防抖保存配置到后端
const saveConfigToBackend = useDebounceFn(async () => { const saveConfigToBackend = useDebounceFn(async () => {
try { try {
await UpdateEditorConfig(config.value); await ConfigService.UpdateEditorConfig(config.value);
logStore.info(t('config.saveSuccess')); logStore.info(t('config.saveSuccess'));
} catch (error) { } catch (error) {
console.error('Failed to save configuration:', error); console.error('Failed to save configuration:', error);
@@ -182,22 +157,11 @@ export const useConfigStore = defineStore('config', () => {
? TabType.TabTypeTab ? TabType.TabTypeTab
: TabType.TabTypeSpaces; : TabType.TabTypeSpaces;
} }
// 设置编码类型
function setEncoding(encoding: string) {
// 验证编码是否有效的EncodingType
const encodingType = encoding as EncodingType;
if (SUPPORTED_ENCODINGS.includes(encodingType)) {
config.value.encoding = encodingType;
} else {
logStore.warning(t('config.invalidEncoding'));
}
}
// 重置为默认配置 // 重置为默认配置
async function resetToDefaults() { async function resetToDefaults() {
try { try {
await ResetConfig(); await ConfigService.ResetConfig();
await loadConfigFromBackend(); await loadConfigFromBackend();
logStore.info(t('config.resetSuccess')); logStore.info(t('config.resetSuccess'));
} catch (error) { } catch (error) {
@@ -217,7 +181,6 @@ export const useConfigStore = defineStore('config', () => {
DEFAULT_FONT_SIZE, DEFAULT_FONT_SIZE,
MIN_TAB_SIZE, MIN_TAB_SIZE,
MAX_TAB_SIZE, MAX_TAB_SIZE,
SUPPORTED_ENCODINGS,
// 核心方法 // 核心方法
loadConfigFromBackend, loadConfigFromBackend,
@@ -230,9 +193,6 @@ export const useConfigStore = defineStore('config', () => {
decreaseFontSize: () => adjustFontSize(-1), decreaseFontSize: () => adjustFontSize(-1),
resetFontSize: () => updateConfig('fontSize', DEFAULT_FONT_SIZE), resetFontSize: () => updateConfig('fontSize', DEFAULT_FONT_SIZE),
// 编码操作
setEncoding,
// Tab操作 // Tab操作
toggleTabIndent: () => updateConfig('enableTabIndent', val => !val), toggleTabIndent: () => updateConfig('enableTabIndent', val => !val),
increaseTabSize: () => adjustTabSize(1), increaseTabSize: () => adjustTabSize(1),

2
go.mod
View File

@@ -34,7 +34,7 @@ require (
github.com/pjbgf/sha1cd v0.3.2 // indirect github.com/pjbgf/sha1cd v0.3.2 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/rivo/uniseg v0.4.7 // indirect github.com/rivo/uniseg v0.4.7 // indirect
github.com/samber/lo v1.49.1 // indirect github.com/samber/lo v1.50.0 // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/skeema/knownhosts v1.3.1 // indirect github.com/skeema/knownhosts v1.3.1 // indirect
github.com/wailsapp/go-webview2 v1.0.21 // indirect github.com/wailsapp/go-webview2 v1.0.21 // indirect

4
go.sum
View File

@@ -86,8 +86,8 @@ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew= github.com/samber/lo v1.50.0 h1:XrG0xOeHs+4FQ8gJR97zDz5uOFMW7OwFWiFVzqopKgY=
github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o= github.com/samber/lo v1.50.0/go.mod h1:RjZyNk6WSnUFRKK6EyOhsRJMqft3G+pg7dCWHQCWvsc=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=

View File

@@ -1,6 +1,8 @@
package models package models
import ( import (
"os"
"path/filepath"
"time" "time"
) )
@@ -14,32 +16,9 @@ const (
TabTypeTab TabType = "tab" TabTypeTab TabType = "tab"
) )
// EncodingType 定义文件编码格式类型
type EncodingType string
const (
// EncodingUTF8 UTF-8编码
EncodingUTF8 EncodingType = "UTF-8"
// EncodingUTF8BOM UTF-8带BOM编码
EncodingUTF8BOM EncodingType = "UTF-8-BOM"
// EncodingUTF16LE UTF-16小端编码
EncodingUTF16LE EncodingType = "UTF-16 LE"
// EncodingUTF16BE UTF-16大端编码
EncodingUTF16BE EncodingType = "UTF-16 BE"
// EncodingISO88591 ISO-8859-1编码
EncodingISO88591 EncodingType = "ISO-8859-1"
// EncodingGB18030 GB18030编码
EncodingGB18030 EncodingType = "GB18030"
// EncodingGBK GBK编码
EncodingGBK EncodingType = "GBK"
// EncodingBig5 Big5编码
EncodingBig5 EncodingType = "Big5"
)
// EditorConfig 定义编辑器配置 // EditorConfig 定义编辑器配置
type EditorConfig struct { type EditorConfig struct {
FontSize int `json:"fontSize"` // 字体大小 FontSize int `json:"fontSize"` // 字体大小
Encoding EncodingType `json:"encoding"` // 文件保存的编码
EnableTabIndent bool `json:"enableTabIndent"` // 是否启用Tab缩进 EnableTabIndent bool `json:"enableTabIndent"` // 是否启用Tab缩进
TabSize int `json:"tabSize"` // Tab大小 TabSize int `json:"tabSize"` // Tab大小
TabType TabType `json:"tabType"` // Tab类型空格或Tab TabType TabType `json:"tabType"` // Tab类型空格或Tab
@@ -56,16 +35,17 @@ const (
LangEnUS LanguageType = "en-US" LangEnUS LanguageType = "en-US"
) )
// PathConfig 定义配置文件路径相关配置 // PathsConfig 路径配置集合
type PathConfig struct { type PathsConfig struct {
RootDir string `json:"rootDir"` // 根目录
ConfigPath string `json:"configPath"` // 配置文件路径 ConfigPath string `json:"configPath"` // 配置文件路径
LogPath string `json:"logPath"` // 日志文件路径
DataPath string `json:"dataPath"` // 数据存储路径
} }
// AppConfig 应用配置 // AppConfig 应用配置 - 包含业务配置和路径配置
type AppConfig struct { type AppConfig struct {
Editor EditorConfig `json:"editor"` // 编辑器配置 Editor EditorConfig `json:"editor"` // 编辑器配置
Paths PathConfig `json:"paths"` // 路径配置 Paths PathsConfig `json:"paths"` // 路径配置
Metadata ConfigMetadata `json:"metadata"` // 配置元数据 Metadata ConfigMetadata `json:"metadata"` // 配置元数据
} }
@@ -77,18 +57,27 @@ type ConfigMetadata struct {
// NewDefaultAppConfig 创建默认应用配置 // NewDefaultAppConfig 创建默认应用配置
func NewDefaultAppConfig() *AppConfig { func NewDefaultAppConfig() *AppConfig {
// 获取用户主目录
homePath, err := os.UserHomeDir()
if err != nil {
homePath = "."
}
// 默认路径配置
rootDir := filepath.Join(homePath, ".voidraft")
return &AppConfig{ return &AppConfig{
Editor: EditorConfig{ Editor: EditorConfig{
FontSize: 13, FontSize: 13,
Encoding: EncodingUTF8,
EnableTabIndent: true, EnableTabIndent: true,
TabSize: 4, TabSize: 4,
TabType: TabTypeSpaces, TabType: TabTypeSpaces,
Language: LangZhCN, Language: LangZhCN,
}, },
Paths: PathConfig{ Paths: PathsConfig{
RootDir: ".voidraft", ConfigPath: filepath.Join(rootDir, "config", "config.json"),
ConfigPath: "config/config.json", LogPath: filepath.Join(rootDir, "logs"),
DataPath: filepath.Join(rootDir, "data"),
}, },
Metadata: ConfigMetadata{ Metadata: ConfigMetadata{
Version: "1.0.0", Version: "1.0.0",

View File

@@ -0,0 +1,93 @@
package config
import (
"fmt"
"os"
"path/filepath"
"sync"
"github.com/wailsapp/wails/v3/pkg/services/log"
)
// ConfigLocator 配置定位器接口
type ConfigLocator interface {
// GetConfigPath 获取配置文件路径
GetConfigPath() string
// SetConfigPath 设置配置文件路径
SetConfigPath(string) error
}
// FileConfigLocator 基于文件的配置定位器
type FileConfigLocator struct {
locationFile string
defaultPath string
logger *log.LoggerService
mu sync.RWMutex
}
// NewFileConfigLocator 创建文件配置定位器
func NewFileConfigLocator(locationFile, defaultPath string, logger *log.LoggerService) *FileConfigLocator {
if logger == nil {
logger = log.New()
}
return &FileConfigLocator{
locationFile: locationFile,
defaultPath: defaultPath,
logger: logger,
}
}
// GetDefaultConfigPath 获取默认配置路径
func GetDefaultConfigPath() string {
homePath, err := os.UserHomeDir()
if err != nil {
return filepath.Join(".voidraft", "config", "config.json")
}
return filepath.Join(homePath, ".voidraft", "config", "config.json")
}
// GetConfigPath 获取配置文件路径
func (fcl *FileConfigLocator) GetConfigPath() string {
fcl.mu.RLock()
defer fcl.mu.RUnlock()
// 尝试从位置文件读取
if _, err := os.Stat(fcl.locationFile); err == nil {
if data, err := os.ReadFile(fcl.locationFile); err == nil && len(data) > 0 {
path := string(data)
// 验证路径目录是否存在
if _, err := os.Stat(filepath.Dir(path)); err == nil {
fcl.logger.Info("ConfigLocator: Using stored path", "path", path)
return path
}
fcl.logger.Error("ConfigLocator: Stored path invalid, using default", "path", path)
}
}
// 返回默认路径
fcl.logger.Info("ConfigLocator: Using default path", "path", fcl.defaultPath)
return fcl.defaultPath
}
// SetConfigPath 设置配置文件路径
func (fcl *FileConfigLocator) SetConfigPath(path string) error {
fcl.mu.Lock()
defer fcl.mu.Unlock()
// 确保位置文件目录存在
if err := os.MkdirAll(filepath.Dir(fcl.locationFile), 0755); err != nil {
return fmt.Errorf("failed to create location directory: %w", err)
}
// 写入位置文件
if err := os.WriteFile(fcl.locationFile, []byte(path), 0644); err != nil {
return fmt.Errorf("failed to write location file: %w", err)
}
fcl.logger.Info("ConfigLocator: Updated config path", "path", path)
return nil
}

View File

@@ -0,0 +1,302 @@
package config
import (
"fmt"
"os"
"path/filepath"
"sync"
"time"
"voidraft/internal/models"
"github.com/wailsapp/wails/v3/pkg/services/log"
)
// ConfigService 提供配置管理功能
type ConfigService struct {
storage ConfigStorage // 配置存储接口
locator ConfigLocator // 配置定位器接口
logger *log.LoggerService
cache *models.AppConfig // 配置缓存
cacheMu sync.RWMutex // 缓存锁
}
type Service struct{}
// NewConfigService 创建新的配置服务实例
func NewConfigService() *ConfigService {
// 初始化日志服务
logger := log.New()
// 获取用户主目录
homePath, err := os.UserHomeDir()
if err != nil {
logger.Error("Config: Failed to get user home directory", "error", err)
homePath = "."
}
// 获取默认配置路径
defaultPath := GetDefaultConfigPath()
// 创建配置定位器
locationFile := filepath.Join(homePath, ".voidraft", "config.location")
locator := NewFileConfigLocator(locationFile, defaultPath, logger)
// 获取实际配置路径
configPath := locator.GetConfigPath()
logger.Info("Config: Using config path", "path", configPath)
// 创建配置存储
storage := NewFileConfigStorage(configPath, logger)
// 构造配置服务实例
service := &ConfigService{
storage: storage,
locator: locator,
logger: logger,
}
// 初始化加载配置
service.loadInitialConfig()
return service
}
// loadInitialConfig 加载初始配置
func (cs *ConfigService) loadInitialConfig() {
// 尝试加载配置
config, err := cs.storage.Load()
if err != nil {
// 如果加载失败,使用默认配置
defaultConfig := models.NewDefaultAppConfig()
defaultConfig.Paths.ConfigPath = cs.storage.GetPath()
// 保存默认配置
if err := cs.storage.Save(*defaultConfig); err != nil {
cs.logger.Error("Config: Failed to save default config", "error", err)
} else {
// 更新缓存
cs.cacheMu.Lock()
cs.cache = defaultConfig
cs.cacheMu.Unlock()
}
} else {
// 确保配置中的路径与实际使用的路径一致
if config.Paths.ConfigPath != cs.storage.GetPath() {
config.Paths.ConfigPath = cs.storage.GetPath()
if err := cs.storage.Save(config); err != nil {
cs.logger.Error("Config: Failed to sync config path", "error", err)
}
}
// 更新缓存
cs.cacheMu.Lock()
cs.cache = &config
cs.cacheMu.Unlock()
}
}
// GetConfig 获取完整应用配置
func (cs *ConfigService) GetConfig() (*models.AppConfig, error) {
// 优先使用缓存
cs.cacheMu.RLock()
if cs.cache != nil {
config := *cs.cache
cs.cacheMu.RUnlock()
return &config, nil
}
cs.cacheMu.RUnlock()
// 缓存不存在,从存储加载
config, err := cs.storage.Load()
if err != nil {
// 加载失败,使用默认配置
defaultConfig := models.NewDefaultAppConfig()
defaultConfig.Paths.ConfigPath = cs.storage.GetPath()
// 保存默认配置
if saveErr := cs.storage.Save(*defaultConfig); saveErr != nil {
cs.logger.Error("Config: Failed to save default config", "error", saveErr)
}
// 更新缓存
cs.cacheMu.Lock()
cs.cache = defaultConfig
cs.cacheMu.Unlock()
return defaultConfig, nil
}
// 更新缓存
cs.cacheMu.Lock()
cs.cache = &config
cs.cacheMu.Unlock()
return &config, nil
}
// SaveConfig 保存完整应用配置
func (cs *ConfigService) SaveConfig(config *models.AppConfig) error {
// 更新配置元数据
config.Metadata.LastUpdated = time.Now()
// 确保ConfigPath与当前路径一致
config.Paths.ConfigPath = cs.storage.GetPath()
// 更新缓存
cs.cacheMu.Lock()
cs.cache = config
cs.cacheMu.Unlock()
// 保存到存储
return cs.storage.Save(*config)
}
// UpdateConfigPath 更新配置文件路径
func (cs *ConfigService) UpdateConfigPath(newPath string) error {
// 如果路径相同,无需更改
if newPath == cs.storage.GetPath() {
return nil
}
// 获取当前配置(优先使用缓存)
config, err := cs.GetConfig()
if err != nil {
return fmt.Errorf("failed to get current config: %w", err)
}
// 更新配置中的路径
config.Paths.ConfigPath = newPath
// 移动到新路径
if err := cs.storage.MoveTo(newPath, *config); err != nil {
return fmt.Errorf("failed to move config to new path: %w", err)
}
// 更新定位器
if err := cs.locator.SetConfigPath(newPath); err != nil {
cs.logger.Error("Config: Failed to update location file", "error", err)
// 继续执行,这不是致命错误
}
cs.logger.Info("Config: Config path updated", "path", newPath)
return nil
}
// UpdatePaths 更新路径配置
func (cs *ConfigService) UpdatePaths(paths models.PathsConfig) error {
config, err := cs.GetConfig()
if err != nil {
return err
}
// 检查配置文件路径是否变更
if paths.ConfigPath != "" && paths.ConfigPath != cs.storage.GetPath() {
// 如果配置路径有变化,使用专门的方法处理
if err := cs.UpdateConfigPath(paths.ConfigPath); err != nil {
return fmt.Errorf("failed to update config path: %w", err)
}
// 更新后重新加载配置
config, err = cs.GetConfig()
if err != nil {
return err
}
}
// 更新其他路径但保持ConfigPath不变
config.Paths.LogPath = paths.LogPath
config.Paths.DataPath = paths.DataPath
// 确保ConfigPath与当前一致
config.Paths.ConfigPath = cs.storage.GetPath()
return cs.SaveConfig(config)
}
// ResetConfig 重置为默认配置
func (cs *ConfigService) ResetConfig() error {
defaultConfig := models.NewDefaultAppConfig()
// 保留当前配置路径
defaultConfig.Paths.ConfigPath = cs.storage.GetPath()
return cs.SaveConfig(defaultConfig)
}
// GetEditorConfig 获取编辑器配置
func (cs *ConfigService) GetEditorConfig() (models.EditorConfig, error) {
config, err := cs.GetConfig()
if err != nil {
return models.EditorConfig{}, err
}
return config.Editor, nil
}
// UpdateEditorConfig 更新编辑器配置
func (cs *ConfigService) UpdateEditorConfig(editorConfig models.EditorConfig) error {
config, err := cs.GetConfig()
if err != nil {
return err
}
config.Editor = editorConfig
return cs.SaveConfig(config)
}
// GetLanguage 获取当前语言设置
func (cs *ConfigService) GetLanguage() (models.LanguageType, error) {
editorConfig, err := cs.GetEditorConfig()
if err != nil {
return "", err
}
return editorConfig.Language, nil
}
// SetLanguage 设置语言
func (cs *ConfigService) SetLanguage(language models.LanguageType) error {
// 验证语言类型有效
if language != models.LangZhCN && language != models.LangEnUS {
return fmt.Errorf("unsupported language: %s", language)
}
config, err := cs.GetConfig()
if err != nil {
return err
}
config.Editor.Language = language
return cs.SaveConfig(config)
}
// GetPaths 获取路径配置
func (cs *ConfigService) GetPaths() (models.PathsConfig, error) {
config, err := cs.GetConfig()
if err != nil {
return models.PathsConfig{}, err
}
return config.Paths, nil
}
// GetMetadata 获取配置元数据
func (cs *ConfigService) GetMetadata() (models.ConfigMetadata, error) {
config, err := cs.GetConfig()
if err != nil {
return models.ConfigMetadata{}, err
}
return config.Metadata, nil
}
// UpdateMetadata 更新配置元数据
func (cs *ConfigService) UpdateMetadata(metadata models.ConfigMetadata) error {
config, err := cs.GetConfig()
if err != nil {
return err
}
config.Metadata = metadata
return cs.SaveConfig(config)
}
// GetConfigPath 获取当前配置文件路径
func (cs *ConfigService) GetConfigPath() string {
return cs.storage.GetPath()
}

View File

@@ -0,0 +1,117 @@
package config
import (
"fmt"
"os"
"path/filepath"
"sync"
"voidraft/internal/models"
"voidraft/internal/services/store"
"github.com/wailsapp/wails/v3/pkg/services/log"
)
// ConfigStorage 配置存储接口
type ConfigStorage interface {
// Load 加载配置
Load() (models.AppConfig, error)
// Save 保存配置
Save(models.AppConfig) error
// GetPath 获取存储路径
GetPath() string
// MoveTo 移动到新路径
MoveTo(string, models.AppConfig) error
}
// FileConfigStorage 基于文件的配置存储
type FileConfigStorage struct {
store *store.Store[models.AppConfig]
currentPath string
logger *log.LoggerService
mu sync.RWMutex
}
// NewFileConfigStorage 创建文件配置存储
func NewFileConfigStorage(path string, logger *log.LoggerService) *FileConfigStorage {
if logger == nil {
logger = log.New()
}
return &FileConfigStorage{
store: store.NewStore[models.AppConfig](store.StoreOption{
FilePath: path,
AutoSave: true,
Logger: logger,
}),
currentPath: path,
logger: logger,
}
}
// Load 加载配置
func (fcs *FileConfigStorage) Load() (models.AppConfig, error) {
fcs.mu.RLock()
defer fcs.mu.RUnlock()
config := fcs.store.Get()
// 检查配置是否为空
if isEmptyConfig(config) {
return models.AppConfig{}, fmt.Errorf("empty config detected")
}
return config, nil
}
// Save 保存配置
func (fcs *FileConfigStorage) Save(config models.AppConfig) error {
fcs.mu.Lock()
defer fcs.mu.Unlock()
return fcs.store.Set(config)
}
// GetPath 获取存储路径
func (fcs *FileConfigStorage) GetPath() string {
fcs.mu.RLock()
defer fcs.mu.RUnlock()
return fcs.currentPath
}
// MoveTo 移动到新路径
func (fcs *FileConfigStorage) MoveTo(newPath string, config models.AppConfig) error {
fcs.mu.Lock()
defer fcs.mu.Unlock()
// 创建目录
if err := os.MkdirAll(filepath.Dir(newPath), 0755); err != nil {
return fmt.Errorf("failed to create directory: %w", err)
}
// 创建新存储
newStore := store.NewStore[models.AppConfig](store.StoreOption{
FilePath: newPath,
AutoSave: true,
Logger: fcs.logger,
})
// 保存到新位置
if err := newStore.Set(config); err != nil {
return fmt.Errorf("failed to save config to new path: %w", err)
}
// 更新状态
fcs.store = newStore
fcs.currentPath = newPath
return nil
}
// isEmptyConfig 检查配置是否为空
func isEmptyConfig(config models.AppConfig) bool {
return config.Editor.FontSize == 0
}

View File

@@ -1,228 +0,0 @@
package services
import (
"fmt"
"os"
"path/filepath"
"time"
"voidraft/internal/models"
"github.com/wailsapp/wails/v3/pkg/services/log"
)
// ConfigService 提供配置管理功能
type ConfigService struct {
store *Store[models.AppConfig]
homePath string
configPath string
logger *log.LoggerService
}
type Service struct{}
// NewConfigService 创建新的配置服务实例
func NewConfigService() *ConfigService {
// 初始化日志服务
logger := log.New()
// 获取用户主目录
homePath, err := os.UserHomeDir()
if err != nil {
logger.Error("Config: Failed to get user home directory", "error", err)
homePath = "."
}
// 创建默认配置
defaultConfig := models.NewDefaultAppConfig()
// 构建完整的配置文件路径
configFilePath := filepath.Join(homePath, defaultConfig.Paths.RootDir, defaultConfig.Paths.ConfigPath)
// 创建Store选项
storeOption := StoreOption{
FilePath: configFilePath,
AutoSave: true,
Logger: logger,
}
// 创建存储服务
store := NewStore[models.AppConfig](storeOption)
// 构造配置服务实例
service := &ConfigService{
store: store,
homePath: homePath,
configPath: configFilePath,
logger: logger,
}
// 检查是否需要设置默认配置
config := store.Get()
if isEmptyConfig(config) {
err := store.Set(*defaultConfig)
if err != nil {
logger.Error("Config: Failed to set default config", "error", err)
}
}
return service
}
// isEmptyConfig 检查配置是否为空
func isEmptyConfig(config models.AppConfig) bool {
// 检查基本字段
if config.Editor.FontSize == 0 && config.Paths.RootDir == "" {
return true
}
return false
}
// GetConfig 获取完整应用配置
func (cs *ConfigService) GetConfig() (*models.AppConfig, error) {
config := cs.store.Get()
// 如果配置为空,返回默认配置
if isEmptyConfig(config) {
defaultConfig := models.NewDefaultAppConfig()
if err := cs.store.Set(*defaultConfig); err != nil {
cs.logger.Error("Config: Failed to save default config", "error", err)
}
return defaultConfig, nil
}
return &config, nil
}
// SaveConfig 保存完整应用配置
func (cs *ConfigService) SaveConfig(config *models.AppConfig) error {
// 更新配置元数据
config.Metadata.LastUpdated = time.Now()
return cs.store.Set(*config)
}
// ResetConfig 重置为默认配置
func (cs *ConfigService) ResetConfig() error {
defaultConfig := models.NewDefaultAppConfig()
err := cs.store.Set(*defaultConfig)
if err != nil {
cs.logger.Error("Config: Failed to save default config", "error", err)
return fmt.Errorf("failed to reset config: %w", err)
}
return nil
}
// GetEditorConfig 获取编辑器配置
func (cs *ConfigService) GetEditorConfig() (models.EditorConfig, error) {
config, err := cs.GetConfig()
if err != nil {
return models.EditorConfig{}, err
}
return config.Editor, nil
}
// UpdateEditorConfig 更新编辑器配置
func (cs *ConfigService) UpdateEditorConfig(editorConfig models.EditorConfig) error {
// 获取当前配置
config, err := cs.GetConfig()
if err != nil {
return err
}
// 更新编辑器配置
config.Editor = editorConfig
config.Metadata.LastUpdated = time.Now()
// 保存更新后的配置
return cs.store.Set(*config)
}
// GetLanguage 获取当前语言设置
func (cs *ConfigService) GetLanguage() (models.LanguageType, error) {
editorConfig, err := cs.GetEditorConfig()
if err != nil {
return "", err
}
return editorConfig.Language, nil
}
// SetLanguage 设置语言
func (cs *ConfigService) SetLanguage(language models.LanguageType) error {
// 验证语言类型有效
if language != models.LangZhCN && language != models.LangEnUS {
return fmt.Errorf("unsupported language: %s", language)
}
// 获取当前配置
config, err := cs.GetConfig()
if err != nil {
return err
}
// 更新语言设置
config.Editor.Language = language
config.Metadata.LastUpdated = time.Now()
// 保存更新后的配置
return cs.store.Set(*config)
}
// GetPathConfig 获取路径配置
func (cs *ConfigService) GetPathConfig() (models.PathConfig, error) {
config, err := cs.GetConfig()
if err != nil {
return models.PathConfig{}, err
}
return config.Paths, nil
}
// UpdatePathConfig 更新路径配置
func (cs *ConfigService) UpdatePathConfig(pathConfig models.PathConfig) error {
// 获取当前配置
config, err := cs.GetConfig()
if err != nil {
return err
}
// 更新路径配置
config.Paths = pathConfig
config.Metadata.LastUpdated = time.Now()
// 保存更新后的配置
return cs.store.Set(*config)
}
// GetMetadata 获取配置元数据
func (cs *ConfigService) GetMetadata() (models.ConfigMetadata, error) {
config, err := cs.GetConfig()
if err != nil {
return models.ConfigMetadata{}, err
}
return config.Metadata, nil
}
// UpdateMetadata 更新配置元数据
func (cs *ConfigService) UpdateMetadata(metadata models.ConfigMetadata) error {
// 获取当前配置
config, err := cs.GetConfig()
if err != nil {
return err
}
// 更新元数据
config.Metadata = metadata
config.Metadata.LastUpdated = time.Now()
// 保存更新后的配置
return cs.store.Set(*config)
}
// OnShutdown 服务关闭时调用
func (cs *ConfigService) OnShutdown() error {
// 如果有未保存的更改,保存数据
if cs.store.HasUnsavedChanges() {
return cs.store.Save()
}
return nil
}

View File

@@ -2,17 +2,18 @@ package services
import ( import (
"github.com/wailsapp/wails/v3/pkg/application" "github.com/wailsapp/wails/v3/pkg/application"
"voidraft/internal/services/config"
) )
// ServiceManager 服务管理器,负责协调各个服务 // ServiceManager 服务管理器,负责协调各个服务
type ServiceManager struct { type ServiceManager struct {
configService *ConfigService configService *config.ConfigService
} }
// NewServiceManager 创建新的服务管理器实例 // NewServiceManager 创建新的服务管理器实例
func NewServiceManager() *ServiceManager { func NewServiceManager() *ServiceManager {
// 初始化配置服务 // 初始化配置服务
configService := NewConfigService() configService := config.NewConfigService()
return &ServiceManager{ return &ServiceManager{
configService: configService, configService: configService,
@@ -27,6 +28,6 @@ func (sm *ServiceManager) GetServices() []application.Service {
} }
// GetConfigService 获取配置服务实例 // GetConfigService 获取配置服务实例
func (sm *ServiceManager) GetConfigService() *ConfigService { func (sm *ServiceManager) GetConfigService() *config.ConfigService {
return sm.configService return sm.configService
} }

View File

@@ -1,4 +1,4 @@
package services package store
import ( import (
"encoding/json" "encoding/json"
@@ -125,10 +125,10 @@ func (s *Store[T]) saveInternal() error {
} }
tempFilePath := tempFile.Name() tempFilePath := tempFile.Name()
defer func() { defer func() {
tempFile.Close() _ = tempFile.Close()
// 如果出错,删除临时文件 // 如果出错,删除临时文件
if err != nil { if err != nil {
os.Remove(tempFilePath) _ = os.Remove(tempFilePath)
} }
}() }()

23
main.go
View File

@@ -28,7 +28,6 @@ func main() {
// 'Assets' configures the asset server with the 'FS' variable pointing to the frontend files. // 'Assets' configures the asset server with the 'FS' variable pointing to the frontend files.
// 'Bind' is a list of Go struct instances. The frontend has access to the methods of these instances. // 'Bind' is a list of Go struct instances. The frontend has access to the methods of these instances.
// 'Mac' options tailor the application when running an macOS. // 'Mac' options tailor the application when running an macOS.
log.Println("Creating Wails application...")
app := application.New(application.Options{ app := application.New(application.Options{
Name: "voidraft", Name: "voidraft",
Description: "voidraft", Description: "voidraft",
@@ -47,10 +46,11 @@ func main() {
// 'BackgroundColour' is the background colour of the window. // 'BackgroundColour' is the background colour of the window.
// 'URL' is the URL that will be loaded into the webview. // 'URL' is the URL that will be loaded into the webview.
log.Println("Creating main window...") log.Println("Creating main window...")
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ mainWindow := app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Title: "voidraft", Title: "voidraft",
Width: 700, Width: 700,
Height: 800, Height: 800,
Hidden: false,
Mac: application.MacWindow{ Mac: application.MacWindow{
InvisibleTitleBarHeight: 50, InvisibleTitleBarHeight: 50,
Backdrop: application.MacBackdropTranslucent, Backdrop: application.MacBackdropTranslucent,
@@ -60,6 +60,22 @@ func main() {
URL: "/", URL: "/",
}) })
systray := app.NewSystemTray()
iconBytes, _ := assets.ReadFile("appicon.png")
systray.SetIcon(iconBytes)
systray.SetLabel("VoidRaft")
menu := app.NewMenu()
menu.Add("显示主窗口").OnClick(func(data *application.Context) {
mainWindow.Show()
})
menu.AddSeparator()
menu.Add("退出").OnClick(func(data *application.Context) {
app.Quit()
})
systray.SetMenu(menu)
systray.AttachWindow(mainWindow)
systray.WindowDebounce(200 * time.Millisecond)
// Create a goroutine that emits an event containing the current time every second. // Create a goroutine that emits an event containing the current time every second.
// The frontend can listen to this event and update the UI accordingly. // The frontend can listen to this event and update the UI accordingly.
go func() { go func() {
@@ -71,13 +87,10 @@ func main() {
}() }()
// Run the application. This blocks until the application has been exited. // Run the application. This blocks until the application has been exited.
log.Println("Starting application event loop...")
err := app.Run() err := app.Run()
// If an error occurred while running the application, log it and exit. // If an error occurred while running the application, log it and exit.
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
log.Println("Application exiting...")
} }