🎨 Refactor save service
This commit is contained in:
@@ -28,11 +28,6 @@ export class AppConfig {
|
||||
*/
|
||||
"metadata": ConfigMetadata;
|
||||
|
||||
/**
|
||||
* 界面语言
|
||||
*/
|
||||
"language": LanguageType;
|
||||
|
||||
/** Creates a new AppConfig instance. */
|
||||
constructor($$source: Partial<AppConfig> = {}) {
|
||||
if (!("editor" in $$source)) {
|
||||
@@ -44,9 +39,6 @@ export class AppConfig {
|
||||
if (!("metadata" in $$source)) {
|
||||
this["metadata"] = (new ConfigMetadata());
|
||||
}
|
||||
if (!("language" in $$source)) {
|
||||
this["language"] = ("" as LanguageType);
|
||||
}
|
||||
|
||||
Object.assign(this, $$source);
|
||||
}
|
||||
@@ -119,7 +111,7 @@ export class EditorConfig {
|
||||
/**
|
||||
* 文件保存的编码
|
||||
*/
|
||||
"encoding": string;
|
||||
"encoding": EncodingType;
|
||||
|
||||
/**
|
||||
* 是否启用Tab缩进
|
||||
@@ -136,13 +128,18 @@ export class EditorConfig {
|
||||
*/
|
||||
"tabType": TabType;
|
||||
|
||||
/**
|
||||
* 界面语言
|
||||
*/
|
||||
"language": LanguageType;
|
||||
|
||||
/** Creates a new EditorConfig instance. */
|
||||
constructor($$source: Partial<EditorConfig> = {}) {
|
||||
if (!("fontSize" in $$source)) {
|
||||
this["fontSize"] = 0;
|
||||
}
|
||||
if (!("encoding" in $$source)) {
|
||||
this["encoding"] = "";
|
||||
this["encoding"] = ("" as EncodingType);
|
||||
}
|
||||
if (!("enableTabIndent" in $$source)) {
|
||||
this["enableTabIndent"] = false;
|
||||
@@ -153,6 +150,9 @@ export class EditorConfig {
|
||||
if (!("tabType" in $$source)) {
|
||||
this["tabType"] = ("" as TabType);
|
||||
}
|
||||
if (!("language" in $$source)) {
|
||||
this["language"] = ("" as LanguageType);
|
||||
}
|
||||
|
||||
Object.assign(this, $$source);
|
||||
}
|
||||
@@ -166,6 +166,56 @@ 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 语言类型定义
|
||||
*/
|
||||
|
@@ -15,10 +15,10 @@ import {Call as $Call, Create as $Create} from "@wailsio/runtime";
|
||||
import * as models$0 from "../models/models.js";
|
||||
|
||||
/**
|
||||
* GetAppConfig 获取应用配置
|
||||
* GetConfig 获取完整应用配置
|
||||
*/
|
||||
export function GetAppConfig(): Promise<models$0.AppConfig | null> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(3361428829) as any;
|
||||
export function GetConfig(): Promise<models$0.AppConfig | null> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(1013336538) as any;
|
||||
let $typingPromise = $resultPromise.then(($result: any) => {
|
||||
return $$createType1($result);
|
||||
}) as any;
|
||||
@@ -38,14 +38,6 @@ export function GetEditorConfig(): Promise<models$0.EditorConfig> & { cancel():
|
||||
return $typingPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* GetFullConfigPath 获取完整的配置文件路径
|
||||
*/
|
||||
export function GetFullConfigPath(): Promise<string> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(38527092) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* GetLanguage 获取当前语言设置
|
||||
*/
|
||||
@@ -55,18 +47,42 @@ export function GetLanguage(): Promise<models$0.LanguageType> & { cancel(): void
|
||||
}
|
||||
|
||||
/**
|
||||
* ResetToDefault 重置为默认配置
|
||||
* GetMetadata 获取配置元数据
|
||||
*/
|
||||
export function ResetToDefault(): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(4057687351) as any;
|
||||
export function GetMetadata(): Promise<models$0.ConfigMetadata> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(3276720617) as any;
|
||||
let $typingPromise = $resultPromise.then(($result: any) => {
|
||||
return $$createType3($result);
|
||||
}) as any;
|
||||
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
|
||||
return $typingPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* GetPathConfig 获取路径配置
|
||||
*/
|
||||
export function GetPathConfig(): Promise<models$0.PathConfig> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(2053285689) as any;
|
||||
let $typingPromise = $resultPromise.then(($result: any) => {
|
||||
return $$createType4($result);
|
||||
}) as any;
|
||||
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
|
||||
return $typingPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* ResetConfig 重置为默认配置
|
||||
*/
|
||||
export function ResetConfig(): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(3593047389) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* SaveAppConfig 保存应用配置
|
||||
* SaveConfig 保存完整应用配置
|
||||
*/
|
||||
export function SaveAppConfig(config: models$0.AppConfig | null): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(2077587650, config) as any;
|
||||
export function SaveConfig(config: models$0.AppConfig | null): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(616684383, config) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
@@ -86,7 +102,25 @@ export function UpdateEditorConfig(editorConfig: models$0.EditorConfig): Promise
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* UpdateMetadata 更新配置元数据
|
||||
*/
|
||||
export function UpdateMetadata(metadata: models$0.ConfigMetadata): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(3353893284, metadata) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* UpdatePathConfig 更新路径配置
|
||||
*/
|
||||
export function UpdatePathConfig(pathConfig: models$0.PathConfig): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(1492772004, pathConfig) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
// Private type creation functions
|
||||
const $$createType0 = models$0.AppConfig.createFrom;
|
||||
const $$createType1 = $Create.Nullable($$createType0);
|
||||
const $$createType2 = models$0.EditorConfig.createFrom;
|
||||
const $$createType3 = models$0.ConfigMetadata.createFrom;
|
||||
const $$createType4 = models$0.PathConfig.createFrom;
|
||||
|
@@ -1,73 +0,0 @@
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
/**
|
||||
* FileService 提供文件操作
|
||||
* @module
|
||||
*/
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import {Call as $Call, Create as $Create} from "@wailsio/runtime";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore: Unused imports
|
||||
import * as time$0 from "../../../time/models.js";
|
||||
|
||||
/**
|
||||
* DeleteFile 删除文件
|
||||
*/
|
||||
export function DeleteFile(filePath: string): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(1771867857, filePath) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* EnsureDir 确保目录存在,如不存在则创建
|
||||
*/
|
||||
export function EnsureDir(dirPath: string): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(2291976369, dirPath) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* FileExists 检查文件是否存在
|
||||
*/
|
||||
export function FileExists(filePath: string): Promise<boolean> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(4264173930, filePath) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* GetFileModTime 获取文件的修改时间
|
||||
*/
|
||||
export function GetFileModTime(filePath: string): Promise<time$0.Time> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(2240854203, filePath) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* LoadJSON 从文件加载JSON数据
|
||||
*/
|
||||
export function LoadJSON(filePath: string, target: any): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(1385779418, filePath, target) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* SaveJSON 保存JSON数据到文件
|
||||
*/
|
||||
export function SaveJSON(filePath: string, data: any): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(3646933935, filePath, data) as any;
|
||||
return $resultPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* SaveJSONWithCheck 保存JSON数据到文件,带并发检查
|
||||
* expectedModTime是期望的文件修改时间,如果为零值则不检查
|
||||
* onConflict是冲突处理函数,如果文件已被修改且此函数不为nil,则调用此函数合并数据
|
||||
*/
|
||||
export function SaveJSONWithCheck(filePath: string, data: any, expectedModTime: time$0.Time, onConflict: any): Promise<void> & { cancel(): void } {
|
||||
let $resultPromise = $Call.ByID(4074237977, filePath, data, expectedModTime, onConflict) as any;
|
||||
return $resultPromise;
|
||||
}
|
@@ -2,8 +2,6 @@
|
||||
// This file is automatically generated. DO NOT EDIT
|
||||
|
||||
import * as ConfigService from "./configservice.js";
|
||||
import * as FileService from "./fileservice.js";
|
||||
export {
|
||||
ConfigService,
|
||||
FileService
|
||||
ConfigService
|
||||
};
|
||||
|
305
frontend/package-lock.json
generated
305
frontend/package-lock.json
generated
@@ -38,13 +38,10 @@
|
||||
"@codemirror/state": "^6.5.2",
|
||||
"@codemirror/view": "^6.36.6",
|
||||
"@lezer/highlight": "^1.2.1",
|
||||
"@primeuix/themes": "^1.0.3",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"@vueuse/core": "^13.1.0",
|
||||
"codemirror": "^6.0.1",
|
||||
"pinia": "^3.0.2",
|
||||
"pinia-plugin-persistedstate": "^4.2.0",
|
||||
"primevue": "^4.3.3",
|
||||
"sass": "^1.87.0",
|
||||
"uuid": "^11.1.0",
|
||||
"vue": "^3.5.13",
|
||||
@@ -53,7 +50,6 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.25.1",
|
||||
"@primevue/auto-import-resolver": "^4.3.3",
|
||||
"@types/node": "^22.15.2",
|
||||
"@vitejs/plugin-vue": "^5.2.3",
|
||||
"@wailsio/runtime": "latest",
|
||||
@@ -1376,6 +1372,7 @@
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@nodelib/fs.stat": "2.0.5",
|
||||
@@ -1389,6 +1386,7 @@
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
|
||||
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
@@ -1398,6 +1396,7 @@
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
|
||||
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@nodelib/fs.scandir": "2.1.5",
|
||||
@@ -1411,7 +1410,10 @@
|
||||
"version": "3.16.2",
|
||||
"resolved": "https://registry.npmmirror.com/@nuxt/kit/-/kit-3.16.2.tgz",
|
||||
"integrity": "sha512-K1SAUo2vweTfudKZzjKsZ5YJoxPLTspR5qz5+G61xtZreLpsdpDYfBseqsIAl5VFLJuszeRpWQ01jP9LfQ6Ksw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"c12": "^3.0.2",
|
||||
"consola": "^3.4.2",
|
||||
@@ -1444,7 +1446,10 @@
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/ignore/-/ignore-7.0.3.tgz",
|
||||
"integrity": "sha512-bAH5jbK/F3T3Jls4I0SO1hmPR0dKU0a7+SY6n1yzRtG54FLO8d6w/nxLFX2Nb7dBu6cCWXPaAME6cYqFUMmuCA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
}
|
||||
@@ -1745,97 +1750,6 @@
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@primeuix/styled": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmmirror.com/@primeuix/styled/-/styled-0.5.1.tgz",
|
||||
"integrity": "sha512-5Ftw/KSauDPClQ8F2qCyCUF7cIUEY4yLNikf0rKV7Vsb8zGYNK0dahQe7CChaR6M2Kn+NA2DSBSk76ZXqj6Uog==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@primeuix/utils": "^0.5.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@primeuix/styles": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/@primeuix/styles/-/styles-1.0.3.tgz",
|
||||
"integrity": "sha512-yHj/Q+fosJ1736Ty5lRbpqhKa9piou+xZPPppNHUDshq0+XhrFwDGggvPGmDAJyUIM+ChM/Nj8lPY/AwTNXAkg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@primeuix/styled": "^0.5.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@primeuix/themes": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/@primeuix/themes/-/themes-1.0.3.tgz",
|
||||
"integrity": "sha512-f/1qadrv5TFMHfvtVv4Y9zjrkeDP2BO/cuzbHBO9DYxKL6YBIPT9BjKec2K4Kg8PcfGm6CAvxAvICadJSWejRw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@primeuix/styled": "^0.5.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@primeuix/utils": {
|
||||
"version": "0.5.3",
|
||||
"resolved": "https://registry.npmmirror.com/@primeuix/utils/-/utils-0.5.3.tgz",
|
||||
"integrity": "sha512-7SGh7734wcF1/uK6RzO6Z6CBjGQ97GDHfpyl2F1G/c7R0z9hkT/V72ypDo82AWcCS7Ta07oIjDpOCTkSVZuEGQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@primevue/auto-import-resolver": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmmirror.com/@primevue/auto-import-resolver/-/auto-import-resolver-4.3.3.tgz",
|
||||
"integrity": "sha512-CwQPlG8IzDySOwF8N0Q0rLv76awCH7SzYt+RHu1AO/HZEsdCorclIoJO6TrJYevenlhxialprRqpIV52McoRpw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@primevue/metadata": "4.3.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@primevue/core": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmmirror.com/@primevue/core/-/core-4.3.3.tgz",
|
||||
"integrity": "sha512-kSkN5oourG7eueoFPIqiNX3oDT/f0I5IRK3uOY/ytz+VzTZp5yuaCN0Nt42ZQpVXjDxMxDvUhIdaXVrjr58NhQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@primeuix/styled": "^0.5.0",
|
||||
"@primeuix/utils": "^0.5.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.11.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@primevue/icons": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmmirror.com/@primevue/icons/-/icons-4.3.3.tgz",
|
||||
"integrity": "sha512-ouQaxHyeFB6MSfEGGbjaK5Qv9efS1xZGetZoU5jcPm090MSYLFtroP1CuK3lZZAQals06TZ6T6qcoNukSHpK5w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@primeuix/utils": "^0.5.1",
|
||||
"@primevue/core": "4.3.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@primevue/metadata": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmmirror.com/@primevue/metadata/-/metadata-4.3.3.tgz",
|
||||
"integrity": "sha512-R1IBTGsYsmOlAy4/dytJW699Iie9B8p8PqbIxDY0GWiMGk8O0tVBYfuCs50w0QtHqmThhLRtSS+eCSOP1ybwSg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.40.0",
|
||||
"resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.0.tgz",
|
||||
@@ -2120,7 +2034,10 @@
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz",
|
||||
"integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
@@ -2132,6 +2049,7 @@
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.7.tgz",
|
||||
"integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/json-schema": {
|
||||
@@ -2656,6 +2574,7 @@
|
||||
"version": "8.14.1",
|
||||
"resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.14.1.tgz",
|
||||
"integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
@@ -2786,6 +2705,7 @@
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz",
|
||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fill-range": "^7.1.1"
|
||||
@@ -2798,7 +2718,10 @@
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/c12/-/c12-3.0.3.tgz",
|
||||
"integrity": "sha512-uC3MacKBb0Z15o5QWCHvHWj5Zv34pGQj9P+iXKSpTuSGFS0KKhUWf4t9AJ+gWjYOdmWCPEGpEzm8sS0iqbpo1w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"chokidar": "^4.0.3",
|
||||
"confbox": "^0.2.2",
|
||||
@@ -2868,7 +2791,10 @@
|
||||
"version": "0.1.6",
|
||||
"resolved": "https://registry.npmmirror.com/citty/-/citty-0.1.6.tgz",
|
||||
"integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"consola": "^3.2.3"
|
||||
}
|
||||
@@ -2919,13 +2845,17 @@
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmmirror.com/confbox/-/confbox-0.2.2.tgz",
|
||||
"integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/consola": {
|
||||
"version": "3.4.2",
|
||||
"resolved": "https://registry.npmmirror.com/consola/-/consola-3.4.2.tgz",
|
||||
"integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^14.18.0 || >=16.10.0"
|
||||
}
|
||||
@@ -3017,23 +2947,23 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/deep-pick-omit": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmmirror.com/deep-pick-omit/-/deep-pick-omit-1.2.1.tgz",
|
||||
"integrity": "sha512-2J6Kc/m3irCeqVG42T+SaUMesaK7oGWaedGnQQK/+O0gYc+2SP5bKh/KKTE7d7SJ+GCA9UUE1GRzh6oDe0EnGw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/defu": {
|
||||
"version": "6.1.4",
|
||||
"resolved": "https://registry.npmmirror.com/defu/-/defu-6.1.4.tgz",
|
||||
"integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==",
|
||||
"license": "MIT"
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/destr": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmmirror.com/destr/-/destr-2.0.5.tgz",
|
||||
"integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==",
|
||||
"license": "MIT"
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/detect-libc": {
|
||||
"version": "1.0.3",
|
||||
@@ -3052,7 +2982,10 @@
|
||||
"version": "16.5.0",
|
||||
"resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-16.5.0.tgz",
|
||||
"integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -3076,7 +3009,10 @@
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/errx/-/errx-0.1.0.tgz",
|
||||
"integrity": "sha512-fZmsRiDNv07K6s2KkKFTiD2aIvECa7++PKyD5NC32tpRw46qZA3sOz+aM+/V9V0GDHxVTKLziveV4JhzBHDp9Q==",
|
||||
"license": "MIT"
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.25.2",
|
||||
@@ -3319,6 +3255,7 @@
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmmirror.com/exsolve/-/exsolve-1.0.5.tgz",
|
||||
"integrity": "sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-deep-equal": {
|
||||
@@ -3332,6 +3269,7 @@
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.3.tgz",
|
||||
"integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@nodelib/fs.stat": "^2.0.2",
|
||||
@@ -3348,6 +3286,7 @@
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"is-glob": "^4.0.1"
|
||||
@@ -3374,6 +3313,7 @@
|
||||
"version": "1.19.1",
|
||||
"resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.19.1.tgz",
|
||||
"integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"reusify": "^1.0.4"
|
||||
@@ -3396,6 +3336,7 @@
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz",
|
||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
@@ -3461,7 +3402,10 @@
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/giget/-/giget-2.0.0.tgz",
|
||||
"integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"citty": "^0.1.6",
|
||||
"consola": "^3.4.0",
|
||||
@@ -3504,7 +3448,10 @@
|
||||
"version": "14.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/globby/-/globby-14.1.0.tgz",
|
||||
"integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@sindresorhus/merge-streams": "^2.1.0",
|
||||
"fast-glob": "^3.3.3",
|
||||
@@ -3524,7 +3471,10 @@
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/ignore/-/ignore-7.0.3.tgz",
|
||||
"integrity": "sha512-bAH5jbK/F3T3Jls4I0SO1hmPR0dKU0a7+SY6n1yzRtG54FLO8d6w/nxLFX2Nb7dBu6cCWXPaAME6cYqFUMmuCA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
}
|
||||
@@ -3622,6 +3572,7 @@
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
@@ -3631,6 +3582,7 @@
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz",
|
||||
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-extglob": "^2.1.1"
|
||||
@@ -3643,6 +3595,7 @@
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz",
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.12.0"
|
||||
@@ -3671,7 +3624,10 @@
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmmirror.com/jiti/-/jiti-2.4.2.tgz",
|
||||
"integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"jiti": "lib/jiti-cli.mjs"
|
||||
}
|
||||
@@ -3680,7 +3636,10 @@
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-9.0.1.tgz",
|
||||
"integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==",
|
||||
"license": "MIT"
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "4.1.0",
|
||||
@@ -3730,7 +3689,10 @@
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmmirror.com/klona/-/klona-2.0.6.tgz",
|
||||
"integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
@@ -3739,7 +3701,10 @@
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/knitwork/-/knitwork-1.2.0.tgz",
|
||||
"integrity": "sha512-xYSH7AvuQ6nXkq42x0v5S8/Iry+cfulBz/DJQzhIyESdLD7425jXsPy4vn5cCXU+HhRN2kVw51Vd1K6/By4BQg==",
|
||||
"license": "MIT"
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/levn": {
|
||||
"version": "0.4.1",
|
||||
@@ -3759,6 +3724,7 @@
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/local-pkg/-/local-pkg-1.1.1.tgz",
|
||||
"integrity": "sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mlly": "^1.7.4",
|
||||
@@ -3815,6 +3781,7 @@
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz",
|
||||
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
@@ -3824,6 +3791,7 @@
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz",
|
||||
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"braces": "^3.0.3",
|
||||
@@ -3856,6 +3824,7 @@
|
||||
"version": "1.7.4",
|
||||
"resolved": "https://registry.npmmirror.com/mlly/-/mlly-1.7.4.tgz",
|
||||
"integrity": "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"acorn": "^8.14.0",
|
||||
@@ -3868,12 +3837,14 @@
|
||||
"version": "0.1.8",
|
||||
"resolved": "https://registry.npmmirror.com/confbox/-/confbox-0.1.8.tgz",
|
||||
"integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/mlly/node_modules/pkg-types": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmmirror.com/pkg-types/-/pkg-types-1.3.1.tgz",
|
||||
"integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"confbox": "^0.1.8",
|
||||
@@ -3931,7 +3902,10 @@
|
||||
"version": "1.6.6",
|
||||
"resolved": "https://registry.npmmirror.com/node-fetch-native/-/node-fetch-native-1.6.6.tgz",
|
||||
"integrity": "sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==",
|
||||
"license": "MIT"
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/normalize-path": {
|
||||
"version": "3.0.0",
|
||||
@@ -3960,7 +3934,10 @@
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmmirror.com/nypm/-/nypm-0.6.0.tgz",
|
||||
"integrity": "sha512-mn8wBFV9G9+UFHIrq+pZ2r2zL4aPau/by3kJb3cM7+5tQHMt6HGQB8FDIeKFYp8o0D2pnH6nVsO88N4AmUxIWg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"citty": "^0.1.6",
|
||||
"consola": "^3.4.0",
|
||||
@@ -3979,7 +3956,10 @@
|
||||
"version": "2.0.11",
|
||||
"resolved": "https://registry.npmmirror.com/ohash/-/ohash-2.0.11.tgz",
|
||||
"integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==",
|
||||
"license": "MIT"
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/optionator": {
|
||||
"version": "0.9.4",
|
||||
@@ -4075,7 +4055,10 @@
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/path-type/-/path-type-6.0.0.tgz",
|
||||
"integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
@@ -4087,6 +4070,7 @@
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz",
|
||||
"integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/perfect-debounce": {
|
||||
@@ -4105,6 +4089,7 @@
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
@@ -4134,34 +4119,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/pinia-plugin-persistedstate": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/pinia-plugin-persistedstate/-/pinia-plugin-persistedstate-4.2.0.tgz",
|
||||
"integrity": "sha512-3buhA7ac+ssbOIx3VRCC8oHkoFwhDM9oHRCjo7nj+O8WUqnW+jRqh7eYT5eS/DNa3H28zp3dYf/nd/Vc8zj8eQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@nuxt/kit": "^3.14.1592",
|
||||
"deep-pick-omit": "^1.2.1",
|
||||
"defu": "^6.1.4",
|
||||
"destr": "^2.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@pinia/nuxt": ">=0.9.0",
|
||||
"pinia": ">=2.3.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@pinia/nuxt": {
|
||||
"optional": true
|
||||
},
|
||||
"pinia": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/pkg-types": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/pkg-types/-/pkg-types-2.1.0.tgz",
|
||||
"integrity": "sha512-wmJwA+8ihJixSoHKxZJRBQG1oY8Yr9pGLzRmSsNms0iNWyHHAlZCa7mmKiFR10YPZuz/2k169JiS/inOjBCZ2A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"confbox": "^0.2.1",
|
||||
@@ -4221,22 +4183,6 @@
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/primevue": {
|
||||
"version": "4.3.3",
|
||||
"resolved": "https://registry.npmmirror.com/primevue/-/primevue-4.3.3.tgz",
|
||||
"integrity": "sha512-nooYVoEz5CdP3EhUkD6c3qTdRmpLHZh75fBynkUkl46K8y5rksHTjdSISiDijwTA5STQIOkyqLb+RM+HQ6nC1Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@primeuix/styled": "^0.5.0",
|
||||
"@primeuix/styles": "^1.0.0",
|
||||
"@primeuix/utils": "^0.5.1",
|
||||
"@primevue/core": "4.3.3",
|
||||
"@primevue/icons": "4.3.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/punycode": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz",
|
||||
@@ -4251,6 +4197,7 @@
|
||||
"version": "0.2.10",
|
||||
"resolved": "https://registry.npmmirror.com/quansync/-/quansync-0.2.10.tgz",
|
||||
"integrity": "sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
@@ -4267,6 +4214,7 @@
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@@ -4287,7 +4235,10 @@
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/rc9/-/rc9-2.1.2.tgz",
|
||||
"integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"defu": "^6.1.4",
|
||||
"destr": "^2.0.3"
|
||||
@@ -4320,6 +4271,7 @@
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/reusify/-/reusify-1.1.0.tgz",
|
||||
"integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"iojs": ">=1.0.0",
|
||||
@@ -4376,6 +4328,7 @@
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz",
|
||||
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@@ -4419,12 +4372,16 @@
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/scule/-/scule-1.3.0.tgz",
|
||||
"integrity": "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==",
|
||||
"license": "MIT"
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.7.1",
|
||||
"resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.1.tgz",
|
||||
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
@@ -4460,7 +4417,10 @@
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/slash/-/slash-5.1.0.tgz",
|
||||
"integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=14.16"
|
||||
},
|
||||
@@ -4490,7 +4450,10 @@
|
||||
"version": "3.9.0",
|
||||
"resolved": "https://registry.npmmirror.com/std-env/-/std-env-3.9.0.tgz",
|
||||
"integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==",
|
||||
"license": "MIT"
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/strip-json-comments": {
|
||||
"version": "3.1.1",
|
||||
@@ -4509,7 +4472,10 @@
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/strip-literal/-/strip-literal-3.0.0.tgz",
|
||||
"integrity": "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"js-tokens": "^9.0.1"
|
||||
},
|
||||
@@ -4552,12 +4518,16 @@
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/tinyexec/-/tinyexec-0.3.2.tgz",
|
||||
"integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==",
|
||||
"license": "MIT"
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/tinyglobby": {
|
||||
"version": "0.2.13",
|
||||
"resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.13.tgz",
|
||||
"integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fdir": "^6.4.4",
|
||||
@@ -4574,6 +4544,7 @@
|
||||
"version": "6.4.4",
|
||||
"resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.4.4.tgz",
|
||||
"integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"picomatch": "^3 || ^4"
|
||||
@@ -4588,6 +4559,7 @@
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.2.tgz",
|
||||
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
@@ -4600,6 +4572,7 @@
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-number": "^7.0.0"
|
||||
@@ -4675,13 +4648,17 @@
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmmirror.com/ufo/-/ufo-1.6.1.tgz",
|
||||
"integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/unctx": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmmirror.com/unctx/-/unctx-2.4.1.tgz",
|
||||
"integrity": "sha512-AbaYw0Nm4mK4qjhns67C+kgxR2YWiwlDBPzxrN8h8C6VtAdCgditAY5Dezu3IJy4XVqAnbrXt9oQJvsn3fyozg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"acorn": "^8.14.0",
|
||||
"estree-walker": "^3.0.3",
|
||||
@@ -4693,7 +4670,10 @@
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-3.0.3.tgz",
|
||||
"integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/estree": "^1.0.0"
|
||||
}
|
||||
@@ -4709,7 +4689,10 @@
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/unicorn-magic/-/unicorn-magic-0.3.0.tgz",
|
||||
"integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
@@ -4721,7 +4704,10 @@
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/unimport/-/unimport-4.2.0.tgz",
|
||||
"integrity": "sha512-mYVtA0nmzrysnYnyb3ALMbByJ+Maosee2+WyE0puXl+Xm2bUwPorPaaeZt0ETfuroPOtG8jj1g/qeFZ6buFnag==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"acorn": "^8.14.1",
|
||||
"escape-string-regexp": "^5.0.0",
|
||||
@@ -4746,7 +4732,10 @@
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
|
||||
"integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -4758,7 +4747,10 @@
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-3.0.3.tgz",
|
||||
"integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/estree": "^1.0.0"
|
||||
}
|
||||
@@ -4767,7 +4759,10 @@
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.2.tgz",
|
||||
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -4779,6 +4774,7 @@
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmmirror.com/unplugin/-/unplugin-2.3.2.tgz",
|
||||
"integrity": "sha512-3n7YA46rROb3zSj8fFxtxC/PqoyvYQ0llwz9wtUPUutr9ig09C8gGo5CWCwHrUzlqC1LLR43kxp5vEIyH1ac1w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"acorn": "^8.14.1",
|
||||
@@ -4793,6 +4789,7 @@
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmmirror.com/unplugin-utils/-/unplugin-utils-0.2.4.tgz",
|
||||
"integrity": "sha512-8U/MtpkPkkk3Atewj1+RcKIjb5WBimZ/WSLhhR3w6SsIj8XJuKTacSP8g+2JhfSGw0Cb125Y+2zA/IzJZDVbhA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"pathe": "^2.0.2",
|
||||
@@ -4809,6 +4806,7 @@
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.2.tgz",
|
||||
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
@@ -4908,6 +4906,7 @@
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.2.tgz",
|
||||
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
@@ -4920,7 +4919,10 @@
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/untyped/-/untyped-2.0.0.tgz",
|
||||
"integrity": "sha512-nwNCjxJTjNuLCgFr42fEak5OcLuB3ecca+9ksPFNvtfYSLpjf+iJqSIaSnIile6ZPbKYxI5k2AfXqeopGudK/g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"citty": "^0.1.6",
|
||||
"defu": "^6.1.4",
|
||||
@@ -5192,6 +5194,7 @@
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmmirror.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz",
|
||||
"integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/which": {
|
||||
|
@@ -42,13 +42,10 @@
|
||||
"@codemirror/state": "^6.5.2",
|
||||
"@codemirror/view": "^6.36.6",
|
||||
"@lezer/highlight": "^1.2.1",
|
||||
"@primeuix/themes": "^1.0.3",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"@vueuse/core": "^13.1.0",
|
||||
"codemirror": "^6.0.1",
|
||||
"pinia": "^3.0.2",
|
||||
"pinia-plugin-persistedstate": "^4.2.0",
|
||||
"primevue": "^4.3.3",
|
||||
"sass": "^1.87.0",
|
||||
"uuid": "^11.1.0",
|
||||
"vue": "^3.5.13",
|
||||
@@ -57,7 +54,6 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.25.1",
|
||||
"@primevue/auto-import-resolver": "^4.3.3",
|
||||
"@types/node": "^22.15.2",
|
||||
"@vitejs/plugin-vue": "^5.2.3",
|
||||
"@wailsio/runtime": "latest",
|
||||
|
@@ -5,6 +5,7 @@ import {useLogStore} from '@/stores/logStore';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { ref } from 'vue';
|
||||
import {SUPPORTED_LOCALES, setLocale, SupportedLocaleType} from '@/i18n';
|
||||
import { EncodingType } from '@/../bindings/voidraft/internal/models/models';
|
||||
|
||||
const editorStore = useEditorStore();
|
||||
const configStore = useConfigStore();
|
||||
@@ -13,6 +14,20 @@ const { t, locale } = useI18n();
|
||||
|
||||
// 语言下拉菜单
|
||||
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) => {
|
||||
@@ -23,6 +38,29 @@ const changeLanguage = (localeCode: SupportedLocaleType) => {
|
||||
// 切换语言菜单显示
|
||||
const toggleLanguageMenu = () => {
|
||||
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>
|
||||
|
||||
@@ -61,19 +99,37 @@ const toggleLanguageMenu = () => {
|
||||
<button class="tab-btn" @click="configStore.increaseTabSize" :disabled="configStore.config.tabSize >= configStore.MAX_TAB_SIZE">+</button>
|
||||
</span>
|
||||
</span>
|
||||
<span class="encoding">{{ t('toolbar.encoding') }}</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="language-selector">
|
||||
<button class="language-btn" @click="toggleLanguageMenu">
|
||||
<div class="selector-dropdown">
|
||||
<button class="selector-btn" @click="toggleLanguageMenu">
|
||||
{{ locale }}
|
||||
<span class="arrow-up">▲</span>
|
||||
<span class="arrow">▲</span>
|
||||
</button>
|
||||
<div class="language-menu" v-if="showLanguageMenu">
|
||||
<div class="selector-menu" v-if="showLanguageMenu">
|
||||
<div
|
||||
v-for="lang in SUPPORTED_LOCALES"
|
||||
:key="lang.code"
|
||||
class="language-option"
|
||||
class="selector-option"
|
||||
:class="{ active: locale === lang.code }"
|
||||
@click="changeLanguage(lang.code)"
|
||||
>
|
||||
@@ -200,17 +256,12 @@ const toggleLanguageMenu = () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.encoding {
|
||||
color: var(--text-muted);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
/* 语言切换样式 */
|
||||
.language-selector {
|
||||
/* 通用下拉选择器样式 */
|
||||
.selector-dropdown {
|
||||
position: relative;
|
||||
|
||||
.language-btn {
|
||||
.selector-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-muted);
|
||||
@@ -226,13 +277,13 @@ const toggleLanguageMenu = () => {
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.arrow-up {
|
||||
.arrow {
|
||||
font-size: 8px;
|
||||
margin-left: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.language-menu {
|
||||
.selector-menu {
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
right: 0;
|
||||
@@ -240,11 +291,13 @@ const toggleLanguageMenu = () => {
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 3px;
|
||||
margin-bottom: 4px;
|
||||
min-width: 100px;
|
||||
min-width: 120px;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
z-index: 1000;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
||||
|
||||
.language-option {
|
||||
.selector-option {
|
||||
padding: 4px 8px;
|
||||
cursor: pointer;
|
||||
font-size: 11px;
|
||||
|
@@ -43,14 +43,12 @@ const i18n = createI18n({
|
||||
GetLanguage().then(lang => {
|
||||
if (lang) {
|
||||
i18n.global.locale = lang as any;
|
||||
document.documentElement.setAttribute('lang', lang);
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error('Failed to get language from backend:', error);
|
||||
// 如果获取失败,使用浏览器语言作为后备
|
||||
const browserLang = getBrowserLanguage();
|
||||
i18n.global.locale = browserLang as any;
|
||||
document.documentElement.setAttribute('lang', browserLang);
|
||||
});
|
||||
|
||||
// 切换语言的方法
|
||||
|
@@ -1,21 +1,11 @@
|
||||
import {createApp} from 'vue';
|
||||
import App from './App.vue';
|
||||
import '@/assets/styles/index.css';
|
||||
import PrimeVue from 'primevue/config';
|
||||
import Aura from '@primeuix/themes/aura';
|
||||
import {createPinia} from 'pinia';
|
||||
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';
|
||||
import i18n from './i18n';
|
||||
|
||||
const pinia = createPinia()
|
||||
pinia.use(piniaPluginPersistedstate)
|
||||
|
||||
const app = createApp(App);
|
||||
app.use(pinia)
|
||||
app.use(PrimeVue, {
|
||||
theme: {
|
||||
preset: Aura
|
||||
}
|
||||
});
|
||||
app.use(i18n);
|
||||
app.mount('#app');
|
||||
|
@@ -3,10 +3,10 @@ import {ref, watch} from 'vue';
|
||||
import {useDebounceFn} from '@vueuse/core';
|
||||
import {
|
||||
GetEditorConfig,
|
||||
ResetToDefault,
|
||||
ResetConfig,
|
||||
UpdateEditorConfig
|
||||
} from '@/../bindings/voidraft/internal/services/configservice';
|
||||
import {EditorConfig, TabType} from '@/../bindings/voidraft/internal/models/models';
|
||||
import {EditorConfig, TabType, EncodingType} from '@/../bindings/voidraft/internal/models/models';
|
||||
import {useLogStore} from './logStore';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
@@ -20,6 +20,26 @@ const DEFAULT_TAB_SIZE = 4;
|
||||
const MIN_TAB_SIZE = 2;
|
||||
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 = {
|
||||
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 },
|
||||
tabType: { values: [TabType.TabTypeSpaces, TabType.TabTypeTab], default: TabType.TabTypeSpaces },
|
||||
encoding: { values: SUPPORTED_ENCODINGS, default: EncodingType.EncodingUTF8 }
|
||||
};
|
||||
|
||||
export const useConfigStore = defineStore('config', () => {
|
||||
// 获取日志store
|
||||
const logStore = useLogStore();
|
||||
@@ -28,7 +48,7 @@ export const useConfigStore = defineStore('config', () => {
|
||||
// 配置状态
|
||||
const config = ref<EditorConfig>(new EditorConfig({
|
||||
fontSize: DEFAULT_FONT_SIZE,
|
||||
encoding: 'UTF-8',
|
||||
encoding: EncodingType.EncodingUTF8,
|
||||
enableTabIndent: true,
|
||||
tabSize: DEFAULT_TAB_SIZE,
|
||||
tabType: TabType.TabTypeSpaces
|
||||
@@ -58,42 +78,44 @@ export const useConfigStore = defineStore('config', () => {
|
||||
let hasChanges = false;
|
||||
|
||||
// 验证字体大小
|
||||
if (config.value.fontSize < MIN_FONT_SIZE || config.value.fontSize > MAX_FONT_SIZE) {
|
||||
if (config.value.fontSize < CONFIG_LIMITS.fontSize.min || config.value.fontSize > CONFIG_LIMITS.fontSize.max) {
|
||||
const oldValue = config.value.fontSize;
|
||||
config.value.fontSize = oldValue < MIN_FONT_SIZE ? MIN_FONT_SIZE :
|
||||
oldValue > MAX_FONT_SIZE ? MAX_FONT_SIZE :
|
||||
DEFAULT_FONT_SIZE;
|
||||
config.value.fontSize = oldValue < CONFIG_LIMITS.fontSize.min ? CONFIG_LIMITS.fontSize.min :
|
||||
oldValue > CONFIG_LIMITS.fontSize.max ? CONFIG_LIMITS.fontSize.max :
|
||||
CONFIG_LIMITS.fontSize.default;
|
||||
|
||||
logStore.warning(t('config.fontSizeFixed', {
|
||||
value: oldValue,
|
||||
fixed: config.value.fontSize
|
||||
}));
|
||||
logStore.warning(t('config.fontSizeFixed'));
|
||||
|
||||
hasChanges = true;
|
||||
}
|
||||
|
||||
// 验证Tab大小
|
||||
if (config.value.tabSize < MIN_TAB_SIZE || config.value.tabSize > MAX_TAB_SIZE) {
|
||||
if (config.value.tabSize < CONFIG_LIMITS.tabSize.min || config.value.tabSize > CONFIG_LIMITS.tabSize.max) {
|
||||
const oldValue = config.value.tabSize;
|
||||
config.value.tabSize = oldValue < MIN_TAB_SIZE ? MIN_TAB_SIZE :
|
||||
oldValue > MAX_TAB_SIZE ? MAX_TAB_SIZE :
|
||||
DEFAULT_TAB_SIZE;
|
||||
config.value.tabSize = oldValue < CONFIG_LIMITS.tabSize.min ? CONFIG_LIMITS.tabSize.min :
|
||||
oldValue > CONFIG_LIMITS.tabSize.max ? CONFIG_LIMITS.tabSize.max :
|
||||
CONFIG_LIMITS.tabSize.default;
|
||||
|
||||
logStore.warning(t('config.tabSizeFixed', {
|
||||
value: oldValue,
|
||||
fixed: config.value.tabSize
|
||||
}));
|
||||
logStore.warning(t('config.tabSizeFixed'));
|
||||
|
||||
hasChanges = true;
|
||||
}
|
||||
|
||||
// 验证TabType是否合法
|
||||
const validTabTypes = [TabType.TabTypeSpaces, TabType.TabTypeTab];
|
||||
if (!validTabTypes.includes(config.value.tabType)) {
|
||||
if (!CONFIG_LIMITS.tabType.values.includes(config.value.tabType)) {
|
||||
const oldValue = config.value.tabType;
|
||||
config.value.tabType = TabType.TabTypeSpaces;
|
||||
config.value.tabType = CONFIG_LIMITS.tabType.default;
|
||||
|
||||
logStore.warning(t('config.tabTypeFixed', { value: oldValue }));
|
||||
logStore.warning(t('config.tabTypeFixed'));
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -121,67 +143,69 @@ export const useConfigStore = defineStore('config', () => {
|
||||
}
|
||||
}, {deep: true});
|
||||
|
||||
// 字体缩放
|
||||
function increaseFontSize() {
|
||||
if (config.value.fontSize < MAX_FONT_SIZE) {
|
||||
config.value.fontSize += 1;
|
||||
// 更新特定配置项的类型安全方法
|
||||
function updateConfig<K extends keyof EditorConfig>(
|
||||
key: K,
|
||||
value: EditorConfig[K] | ((currentValue: EditorConfig[K]) => EditorConfig[K])
|
||||
) {
|
||||
if (typeof value === 'function') {
|
||||
const currentValue = config.value[key];
|
||||
const fn = value as (val: EditorConfig[K]) => EditorConfig[K];
|
||||
config.value[key] = fn(currentValue);
|
||||
} else {
|
||||
config.value[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
// 字体缩小
|
||||
function decreaseFontSize() {
|
||||
if (config.value.fontSize > MIN_FONT_SIZE) {
|
||||
config.value.fontSize -= 1;
|
||||
}
|
||||
// 用于数字类型配置的增减方法
|
||||
function adjustFontSize(amount: number) {
|
||||
let newValue = config.value.fontSize + amount;
|
||||
|
||||
if (newValue < MIN_FONT_SIZE) newValue = MIN_FONT_SIZE;
|
||||
if (newValue > MAX_FONT_SIZE) newValue = MAX_FONT_SIZE;
|
||||
|
||||
config.value.fontSize = newValue;
|
||||
}
|
||||
|
||||
// 重置字体大小
|
||||
function resetFontSize() {
|
||||
config.value.fontSize = DEFAULT_FONT_SIZE;
|
||||
function adjustTabSize(amount: number) {
|
||||
let newValue = config.value.tabSize + amount;
|
||||
|
||||
if (newValue < MIN_TAB_SIZE) newValue = MIN_TAB_SIZE;
|
||||
if (newValue > MAX_TAB_SIZE) newValue = MAX_TAB_SIZE;
|
||||
|
||||
config.value.tabSize = newValue;
|
||||
}
|
||||
|
||||
// 设置编码
|
||||
function setEncoding(newEncoding: string) {
|
||||
config.value.encoding = newEncoding;
|
||||
}
|
||||
|
||||
// Tab相关方法
|
||||
function toggleTabIndent() {
|
||||
config.value.enableTabIndent = !config.value.enableTabIndent;
|
||||
}
|
||||
|
||||
// 增加Tab大小
|
||||
function increaseTabSize() {
|
||||
if (config.value.tabSize < MAX_TAB_SIZE) {
|
||||
config.value.tabSize += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 减少Tab大小
|
||||
function decreaseTabSize() {
|
||||
if (config.value.tabSize > MIN_TAB_SIZE) {
|
||||
config.value.tabSize -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 切换Tab类型(空格或制表符)
|
||||
// Tab相关类型安全的配置切换
|
||||
function toggleTabType() {
|
||||
config.value.tabType = config.value.tabType === TabType.TabTypeSpaces
|
||||
? TabType.TabTypeTab
|
||||
config.value.tabType = config.value.tabType === TabType.TabTypeSpaces
|
||||
? TabType.TabTypeTab
|
||||
: 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() {
|
||||
try {
|
||||
await ResetToDefault();
|
||||
await loadConfigFromBackend();
|
||||
await ResetConfig();
|
||||
await loadConfigFromBackend();
|
||||
logStore.info(t('config.resetSuccess'));
|
||||
} catch (error) {
|
||||
console.error('Failed to reset configuration:', error);
|
||||
logStore.error(t('config.resetFailed'));
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
// 状态
|
||||
config,
|
||||
@@ -193,18 +217,26 @@ export const useConfigStore = defineStore('config', () => {
|
||||
DEFAULT_FONT_SIZE,
|
||||
MIN_TAB_SIZE,
|
||||
MAX_TAB_SIZE,
|
||||
SUPPORTED_ENCODINGS,
|
||||
|
||||
// 方法
|
||||
// 核心方法
|
||||
loadConfigFromBackend,
|
||||
saveConfigToBackend,
|
||||
updateConfig,
|
||||
resetToDefaults,
|
||||
|
||||
// 字体大小方法
|
||||
increaseFontSize: () => adjustFontSize(1),
|
||||
decreaseFontSize: () => adjustFontSize(-1),
|
||||
resetFontSize: () => updateConfig('fontSize', DEFAULT_FONT_SIZE),
|
||||
|
||||
// 编码操作
|
||||
setEncoding,
|
||||
increaseFontSize,
|
||||
decreaseFontSize,
|
||||
resetFontSize,
|
||||
toggleTabIndent,
|
||||
increaseTabSize,
|
||||
decreaseTabSize,
|
||||
toggleTabType,
|
||||
resetToDefaults
|
||||
|
||||
// Tab操作
|
||||
toggleTabIndent: () => updateConfig('enableTabIndent', val => !val),
|
||||
increaseTabSize: () => adjustTabSize(1),
|
||||
decreaseTabSize: () => adjustTabSize(-1),
|
||||
toggleTabType
|
||||
};
|
||||
});
|
@@ -43,9 +43,6 @@ export const useEditorStore = defineStore('editor', () => {
|
||||
documentStats,
|
||||
editorView,
|
||||
|
||||
// 配置引用
|
||||
config: configStore.config,
|
||||
|
||||
// 方法
|
||||
setEditorView,
|
||||
updateDocumentStats,
|
||||
|
@@ -1,7 +1,6 @@
|
||||
import {defineConfig, loadEnv} from 'vite';
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
import Components from 'unplugin-vue-components/vite';
|
||||
import {PrimeVueResolver} from '@primevue/auto-import-resolver';
|
||||
import * as path from 'path';
|
||||
|
||||
export default defineConfig(({mode}: { mode: string }): object => {
|
||||
@@ -19,7 +18,7 @@ export default defineConfig(({mode}: { mode: string }): object => {
|
||||
Components({
|
||||
dts: true,
|
||||
dirs: ['src/components'],
|
||||
resolvers: [PrimeVueResolver()],
|
||||
resolvers: [],
|
||||
})
|
||||
],
|
||||
esbuild: {
|
||||
|
@@ -14,13 +14,36 @@ const (
|
||||
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 定义编辑器配置
|
||||
type EditorConfig struct {
|
||||
FontSize int `json:"fontSize"` // 字体大小
|
||||
Encoding string `json:"encoding"` // 文件保存的编码
|
||||
EnableTabIndent bool `json:"enableTabIndent"` // 是否启用Tab缩进
|
||||
TabSize int `json:"tabSize"` // Tab大小
|
||||
TabType TabType `json:"tabType"` // Tab类型(空格或Tab)
|
||||
FontSize int `json:"fontSize"` // 字体大小
|
||||
Encoding EncodingType `json:"encoding"` // 文件保存的编码
|
||||
EnableTabIndent bool `json:"enableTabIndent"` // 是否启用Tab缩进
|
||||
TabSize int `json:"tabSize"` // Tab大小
|
||||
TabType TabType `json:"tabType"` // Tab类型(空格或Tab)
|
||||
Language LanguageType `json:"language"` // 界面语言
|
||||
}
|
||||
|
||||
// LanguageType 语言类型定义
|
||||
@@ -44,7 +67,6 @@ type AppConfig struct {
|
||||
Editor EditorConfig `json:"editor"` // 编辑器配置
|
||||
Paths PathConfig `json:"paths"` // 路径配置
|
||||
Metadata ConfigMetadata `json:"metadata"` // 配置元数据
|
||||
Language LanguageType `json:"language"` // 界面语言
|
||||
}
|
||||
|
||||
// ConfigMetadata 配置元数据
|
||||
@@ -58,10 +80,11 @@ func NewDefaultAppConfig() *AppConfig {
|
||||
return &AppConfig{
|
||||
Editor: EditorConfig{
|
||||
FontSize: 13,
|
||||
Encoding: "UTF-8",
|
||||
Encoding: EncodingUTF8,
|
||||
EnableTabIndent: true,
|
||||
TabSize: 4,
|
||||
TabType: TabTypeSpaces,
|
||||
Language: LangZhCN,
|
||||
},
|
||||
Paths: PathConfig{
|
||||
RootDir: ".voidraft",
|
||||
@@ -71,6 +94,5 @@ func NewDefaultAppConfig() *AppConfig {
|
||||
Version: "1.0.0",
|
||||
LastUpdated: time.Now(),
|
||||
},
|
||||
Language: LangZhCN,
|
||||
}
|
||||
}
|
||||
|
@@ -1,340 +1,150 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"voidraft/internal/models"
|
||||
|
||||
"github.com/wailsapp/wails/v3/pkg/services/log"
|
||||
)
|
||||
|
||||
// ConfigService 提供配置管理功能
|
||||
type ConfigService struct {
|
||||
fileService *FileService
|
||||
configPath string
|
||||
rootDir string
|
||||
homePath string
|
||||
config *models.AppConfig
|
||||
lastModTime time.Time // 上次读取配置文件的修改时间
|
||||
store *Store[models.AppConfig]
|
||||
homePath string
|
||||
configPath string
|
||||
logger *log.LoggerService
|
||||
}
|
||||
type Service struct{}
|
||||
|
||||
// NewConfigService 创建新的配置服务实例
|
||||
func NewConfigService(fileService *FileService) *ConfigService {
|
||||
func NewConfigService() *ConfigService {
|
||||
// 初始化日志服务
|
||||
logger := log.New()
|
||||
|
||||
// 获取用户主目录
|
||||
homePath, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
log.Printf("Failed to get user home directory: %v", err)
|
||||
logger.Error("Config: Failed to get user home directory", "error", err)
|
||||
homePath = "."
|
||||
}
|
||||
|
||||
log.Printf("User home directory: %s", homePath)
|
||||
|
||||
// 创建默认配置
|
||||
defaultConfig := models.NewDefaultAppConfig()
|
||||
|
||||
// 构造服务实例
|
||||
service := &ConfigService{
|
||||
fileService: fileService,
|
||||
rootDir: defaultConfig.Paths.RootDir,
|
||||
configPath: defaultConfig.Paths.ConfigPath,
|
||||
homePath: homePath,
|
||||
config: defaultConfig,
|
||||
// 构建完整的配置文件路径
|
||||
configFilePath := filepath.Join(homePath, defaultConfig.Paths.RootDir, defaultConfig.Paths.ConfigPath)
|
||||
|
||||
// 创建Store选项
|
||||
storeOption := StoreOption{
|
||||
FilePath: configFilePath,
|
||||
AutoSave: true,
|
||||
Logger: logger,
|
||||
}
|
||||
|
||||
// 初始化配置目录和文件
|
||||
service.initializeConfig()
|
||||
// 创建存储服务
|
||||
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
|
||||
}
|
||||
|
||||
// initializeConfig 初始化配置目录和文件
|
||||
func (cs *ConfigService) initializeConfig() {
|
||||
// 确保配置目录存在
|
||||
dirPath := filepath.Join(cs.homePath, cs.rootDir)
|
||||
log.Printf("Creating config directory: %s", dirPath)
|
||||
|
||||
// 确保主目录存在
|
||||
if err := cs.fileService.EnsureDir(dirPath); err != nil {
|
||||
log.Printf("Failed to create config directory: %v", err)
|
||||
return
|
||||
// isEmptyConfig 检查配置是否为空
|
||||
func isEmptyConfig(config models.AppConfig) bool {
|
||||
// 检查基本字段
|
||||
if config.Editor.FontSize == 0 && config.Paths.RootDir == "" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 确保配置文件所在目录存在
|
||||
configDir := filepath.Dir(cs.GetFullConfigPath())
|
||||
if configDir != dirPath {
|
||||
if err := cs.fileService.EnsureDir(configDir); err != nil {
|
||||
log.Printf("Failed to create config file directory: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
// GetConfig 获取完整应用配置
|
||||
func (cs *ConfigService) GetConfig() (*models.AppConfig, error) {
|
||||
config := cs.store.Get()
|
||||
|
||||
// 检查配置文件是否存在
|
||||
configFilePath := cs.GetFullConfigPath()
|
||||
log.Printf("Config file path: %s", configFilePath)
|
||||
|
||||
if !cs.fileService.FileExists(configFilePath) {
|
||||
log.Printf("Config file not found, creating default config")
|
||||
// 创建默认配置文件
|
||||
// 如果配置为空,返回默认配置
|
||||
if isEmptyConfig(config) {
|
||||
defaultConfig := models.NewDefaultAppConfig()
|
||||
if err := cs.saveAppConfig(defaultConfig); err != nil {
|
||||
log.Printf("Failed to save default config: %v", err)
|
||||
}
|
||||
} else {
|
||||
// 加载现有配置
|
||||
log.Printf("Loading existing config file")
|
||||
existingConfig, modTime, err := cs.loadAppConfigWithModTime()
|
||||
if err != nil {
|
||||
log.Printf("Failed to load existing config, using default: %v", err)
|
||||
} else {
|
||||
cs.config = existingConfig
|
||||
cs.lastModTime = modTime
|
||||
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
|
||||
}
|
||||
|
||||
// GetFullConfigPath 获取完整的配置文件路径
|
||||
func (cs *ConfigService) GetFullConfigPath() string {
|
||||
return filepath.Join(cs.homePath, cs.rootDir, cs.configPath)
|
||||
}
|
||||
|
||||
// mergeConfigs 合并两个配置,实现智能合并策略
|
||||
func (cs *ConfigService) mergeConfigs(currentConfig *models.AppConfig, existingData []byte) (*models.AppConfig, error) {
|
||||
// 解析磁盘配置
|
||||
var diskConfig models.AppConfig
|
||||
if err := json.Unmarshal(existingData, &diskConfig); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse existing config: %w", err)
|
||||
}
|
||||
|
||||
// 创建合并后的配置(基于磁盘配置)
|
||||
mergedConfig := diskConfig
|
||||
|
||||
// ===== 1. 编辑器配置合并策略 =====
|
||||
// 编辑器配置是用户最直接的操作,总是保留当前配置
|
||||
log.Printf("Merge: Preserving current editor config")
|
||||
mergedConfig.Editor = currentConfig.Editor
|
||||
|
||||
// ===== 2. 路径配置合并策略 =====
|
||||
// 如果当前实例已经修改了根目录(非默认值),保留当前配置
|
||||
defaultConfig := models.NewDefaultAppConfig()
|
||||
|
||||
if currentConfig.Paths.RootDir != defaultConfig.Paths.RootDir &&
|
||||
currentConfig.Paths.RootDir != diskConfig.Paths.RootDir {
|
||||
log.Printf("Merge: Using custom root directory from current config: %s", currentConfig.Paths.RootDir)
|
||||
mergedConfig.Paths.RootDir = currentConfig.Paths.RootDir
|
||||
} else {
|
||||
log.Printf("Merge: Using root directory from disk config: %s", diskConfig.Paths.RootDir)
|
||||
}
|
||||
|
||||
// 对配置路径采用相同策略
|
||||
if currentConfig.Paths.ConfigPath != defaultConfig.Paths.ConfigPath &&
|
||||
currentConfig.Paths.ConfigPath != diskConfig.Paths.ConfigPath {
|
||||
log.Printf("Merge: Using custom config path from current config: %s", currentConfig.Paths.ConfigPath)
|
||||
mergedConfig.Paths.ConfigPath = currentConfig.Paths.ConfigPath
|
||||
} else {
|
||||
log.Printf("Merge: Using config path from disk config: %s", diskConfig.Paths.ConfigPath)
|
||||
}
|
||||
|
||||
// ===== 3. 元数据合并策略 =====
|
||||
// 版本号使用较高的版本
|
||||
if compareVersions(currentConfig.Metadata.Version, diskConfig.Metadata.Version) > 0 {
|
||||
log.Printf("Merge: Using higher version from current config: %s", currentConfig.Metadata.Version)
|
||||
mergedConfig.Metadata.Version = currentConfig.Metadata.Version
|
||||
} else {
|
||||
log.Printf("Merge: Using version from disk config: %s", diskConfig.Metadata.Version)
|
||||
}
|
||||
|
||||
// 更新时间总是使用当前时间
|
||||
mergedConfig.Metadata.LastUpdated = time.Now()
|
||||
|
||||
// ===== 4. 其他配置项 =====
|
||||
|
||||
log.Printf("Merge: Completed using comprehensive merge strategy")
|
||||
return &mergedConfig, nil
|
||||
}
|
||||
|
||||
// compareVersions 比较两个版本号
|
||||
// 返回: 1 如果v1>v2, 0 如果v1=v2, -1 如果v1<v2
|
||||
func compareVersions(v1, v2 string) int {
|
||||
// 分割版本号
|
||||
parts1 := strings.Split(v1, ".")
|
||||
parts2 := strings.Split(v2, ".")
|
||||
|
||||
// 确保两个版本号有相同数量的部分
|
||||
for len(parts1) < len(parts2) {
|
||||
parts1 = append(parts1, "0")
|
||||
}
|
||||
for len(parts2) < len(parts1) {
|
||||
parts2 = append(parts2, "0")
|
||||
}
|
||||
|
||||
// 逐个比较各部分
|
||||
for i := 0; i < len(parts1); i++ {
|
||||
num1, err1 := strconv.Atoi(parts1[i])
|
||||
num2, err2 := strconv.Atoi(parts2[i])
|
||||
|
||||
// 如果有无法解析为数字的部分,按字符串比较
|
||||
if err1 != nil || err2 != nil {
|
||||
if parts1[i] > parts2[i] {
|
||||
return 1
|
||||
} else if parts1[i] < parts2[i] {
|
||||
return -1
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// 数值比较
|
||||
if num1 > num2 {
|
||||
return 1
|
||||
} else if num1 < num2 {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
// 所有部分都相等
|
||||
return 0
|
||||
}
|
||||
|
||||
// loadAppConfigWithModTime 从文件加载配置,同时返回文件修改时间
|
||||
func (cs *ConfigService) loadAppConfigWithModTime() (*models.AppConfig, time.Time, error) {
|
||||
config := &models.AppConfig{}
|
||||
configPath := cs.GetFullConfigPath()
|
||||
|
||||
// 先获取文件修改时间
|
||||
modTime, err := cs.fileService.GetFileModTime(configPath)
|
||||
if err != nil {
|
||||
return nil, time.Time{}, fmt.Errorf("failed to get config file modification time: %w", err)
|
||||
}
|
||||
|
||||
// 然后加载文件内容
|
||||
if err := cs.fileService.LoadJSON(configPath, config); err != nil {
|
||||
return nil, time.Time{}, fmt.Errorf("failed to load config file: %w", err)
|
||||
}
|
||||
|
||||
return config, modTime, nil
|
||||
}
|
||||
|
||||
// handleConfigConflict 处理配置冲突的回调函数
|
||||
func (cs *ConfigService) handleConfigConflict(existingData []byte) (interface{}, error) {
|
||||
// 如果没有当前配置,无法合并
|
||||
if cs.config == nil {
|
||||
return nil, fmt.Errorf("no current config to merge")
|
||||
}
|
||||
|
||||
// 合并配置
|
||||
mergedConfig, err := cs.mergeConfigs(cs.config, existingData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 更新内存中的配置
|
||||
cs.config = mergedConfig
|
||||
|
||||
return mergedConfig, nil
|
||||
}
|
||||
|
||||
// 内部方法:从文件加载配置
|
||||
func (cs *ConfigService) loadAppConfig() (*models.AppConfig, error) {
|
||||
config, modTime, err := cs.loadAppConfigWithModTime()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 更新最后修改时间
|
||||
cs.lastModTime = modTime
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// 内部方法:保存配置到文件
|
||||
func (cs *ConfigService) saveAppConfig(config *models.AppConfig) error {
|
||||
// SaveConfig 保存完整应用配置
|
||||
func (cs *ConfigService) SaveConfig(config *models.AppConfig) error {
|
||||
// 更新配置元数据
|
||||
config.Metadata.LastUpdated = time.Now()
|
||||
|
||||
// 保存到文件,使用乐观并发控制
|
||||
configPath := cs.GetFullConfigPath()
|
||||
log.Printf("saveAppConfig: Saving to %s with last mod time: %s",
|
||||
configPath, cs.lastModTime.Format(time.RFC3339))
|
||||
return cs.store.Set(*config)
|
||||
}
|
||||
|
||||
err := cs.fileService.SaveJSONWithCheck(
|
||||
configPath,
|
||||
config,
|
||||
cs.lastModTime,
|
||||
cs.handleConfigConflict,
|
||||
)
|
||||
// ResetConfig 重置为默认配置
|
||||
func (cs *ConfigService) ResetConfig() error {
|
||||
defaultConfig := models.NewDefaultAppConfig()
|
||||
|
||||
err := cs.store.Set(*defaultConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to save config file: %w", err)
|
||||
}
|
||||
|
||||
// 更新内存中的配置和最后修改时间
|
||||
cs.config = config
|
||||
|
||||
// 获取最新的修改时间
|
||||
newModTime, err := cs.fileService.GetFileModTime(configPath)
|
||||
if err == nil {
|
||||
cs.lastModTime = newModTime
|
||||
cs.logger.Error("Config: Failed to save default config", "error", err)
|
||||
return fmt.Errorf("failed to reset config: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAppConfig 获取应用配置
|
||||
func (cs *ConfigService) GetAppConfig() (*models.AppConfig, error) {
|
||||
// 返回内存中的配置副本
|
||||
if cs.config != nil {
|
||||
return cs.config, nil
|
||||
}
|
||||
|
||||
// 从文件加载
|
||||
config, err := cs.loadAppConfig()
|
||||
// GetEditorConfig 获取编辑器配置
|
||||
func (cs *ConfigService) GetEditorConfig() (models.EditorConfig, error) {
|
||||
config, err := cs.GetConfig()
|
||||
if err != nil {
|
||||
log.Printf("GetAppConfig: Failed to load config: %v", err)
|
||||
// 使用默认配置
|
||||
defaultConfig := models.NewDefaultAppConfig()
|
||||
cs.config = defaultConfig
|
||||
|
||||
// 保存默认配置到文件
|
||||
if err := cs.saveAppConfig(defaultConfig); err != nil {
|
||||
log.Printf("GetAppConfig: Failed to save default config: %v", err)
|
||||
}
|
||||
|
||||
return defaultConfig, nil
|
||||
return models.EditorConfig{}, err
|
||||
}
|
||||
|
||||
// 更新内存中的配置
|
||||
cs.config = config
|
||||
return config, nil
|
||||
return config.Editor, nil
|
||||
}
|
||||
|
||||
// SaveAppConfig 保存应用配置
|
||||
func (cs *ConfigService) SaveAppConfig(config *models.AppConfig) error {
|
||||
return cs.saveAppConfig(config)
|
||||
// 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) {
|
||||
// 如果内存中已有配置,直接返回
|
||||
if cs.config != nil {
|
||||
return cs.config.Language, nil
|
||||
}
|
||||
|
||||
// 否则从文件加载
|
||||
config, err := cs.loadAppConfig()
|
||||
editorConfig, err := cs.GetEditorConfig()
|
||||
if err != nil {
|
||||
log.Printf("GetLanguage: Failed to load config: %v", err)
|
||||
// 使用默认配置
|
||||
defaultConfig := models.NewDefaultAppConfig()
|
||||
cs.config = defaultConfig
|
||||
return defaultConfig.Language, nil
|
||||
return "", err
|
||||
}
|
||||
|
||||
// 更新内存中的配置
|
||||
cs.config = config
|
||||
log.Printf("GetLanguage: Retrieved language: %s", config.Language)
|
||||
return config.Language, nil
|
||||
return editorConfig.Language, nil
|
||||
}
|
||||
|
||||
// SetLanguage 设置语言
|
||||
@@ -344,89 +154,75 @@ func (cs *ConfigService) SetLanguage(language models.LanguageType) error {
|
||||
return fmt.Errorf("unsupported language: %s", language)
|
||||
}
|
||||
|
||||
// 如果内存中已有配置,直接更新
|
||||
if cs.config != nil {
|
||||
log.Printf("SetLanguage: Updating language to: %s", language)
|
||||
cs.config.Language = language
|
||||
return cs.saveAppConfig(cs.config)
|
||||
}
|
||||
|
||||
// 没有内存中的配置,需要先加载
|
||||
config, err := cs.loadAppConfig()
|
||||
// 获取当前配置
|
||||
config, err := cs.GetConfig()
|
||||
if err != nil {
|
||||
log.Printf("SetLanguage: Failed to load config: %v", err)
|
||||
// 使用默认配置
|
||||
config = models.NewDefaultAppConfig()
|
||||
return err
|
||||
}
|
||||
|
||||
// 更新语言配置
|
||||
config.Language = language
|
||||
// 更新语言设置
|
||||
config.Editor.Language = language
|
||||
config.Metadata.LastUpdated = time.Now()
|
||||
|
||||
// 保存到文件
|
||||
return cs.saveAppConfig(config)
|
||||
// 保存更新后的配置
|
||||
return cs.store.Set(*config)
|
||||
}
|
||||
|
||||
// UpdateEditorConfig 更新编辑器配置
|
||||
func (cs *ConfigService) UpdateEditorConfig(editorConfig models.EditorConfig) error {
|
||||
// 如果内存中已有配置,直接更新
|
||||
if cs.config != nil {
|
||||
log.Printf("UpdateEditorConfig: Updating in-memory editor config: %+v", editorConfig)
|
||||
cs.config.Editor = editorConfig
|
||||
return cs.saveAppConfig(cs.config)
|
||||
}
|
||||
|
||||
// 没有内存中的配置,需要先加载
|
||||
config, err := cs.loadAppConfig()
|
||||
// GetPathConfig 获取路径配置
|
||||
func (cs *ConfigService) GetPathConfig() (models.PathConfig, error) {
|
||||
config, err := cs.GetConfig()
|
||||
if err != nil {
|
||||
log.Printf("UpdateEditorConfig: Failed to load config: %v", err)
|
||||
// 使用默认配置
|
||||
config = models.NewDefaultAppConfig()
|
||||
return models.PathConfig{}, err
|
||||
}
|
||||
|
||||
// 更新编辑器配置
|
||||
config.Editor = editorConfig
|
||||
|
||||
// 保存到文件
|
||||
return cs.saveAppConfig(config)
|
||||
return config.Paths, nil
|
||||
}
|
||||
|
||||
// GetEditorConfig 获取编辑器配置
|
||||
func (cs *ConfigService) GetEditorConfig() (models.EditorConfig, error) {
|
||||
// 如果内存中已有配置,直接返回
|
||||
if cs.config != nil {
|
||||
return cs.config.Editor, nil
|
||||
}
|
||||
|
||||
// 否则从文件加载
|
||||
config, err := cs.loadAppConfig()
|
||||
// UpdatePathConfig 更新路径配置
|
||||
func (cs *ConfigService) UpdatePathConfig(pathConfig models.PathConfig) error {
|
||||
// 获取当前配置
|
||||
config, err := cs.GetConfig()
|
||||
if err != nil {
|
||||
log.Printf("GetEditorConfig: Failed to load config: %v", err)
|
||||
// 使用默认配置
|
||||
defaultConfig := models.NewDefaultAppConfig()
|
||||
cs.config = defaultConfig
|
||||
return defaultConfig.Editor, nil
|
||||
return err
|
||||
}
|
||||
|
||||
// 更新内存中的配置
|
||||
cs.config = config
|
||||
log.Printf("GetEditorConfig: Retrieved editor config: %+v", config.Editor)
|
||||
return config.Editor, nil
|
||||
// 更新路径配置
|
||||
config.Paths = pathConfig
|
||||
config.Metadata.LastUpdated = time.Now()
|
||||
|
||||
// 保存更新后的配置
|
||||
return cs.store.Set(*config)
|
||||
}
|
||||
|
||||
// ResetToDefault 重置为默认配置
|
||||
func (cs *ConfigService) ResetToDefault() error {
|
||||
// 创建默认配置
|
||||
defaultConfig := models.NewDefaultAppConfig()
|
||||
|
||||
log.Printf("ResetToDefault: Resetting to default config")
|
||||
|
||||
// 保存到文件
|
||||
err := cs.saveAppConfig(defaultConfig)
|
||||
// GetMetadata 获取配置元数据
|
||||
func (cs *ConfigService) GetMetadata() (models.ConfigMetadata, error) {
|
||||
config, err := cs.GetConfig()
|
||||
if err != nil {
|
||||
log.Printf("ResetToDefault: Failed to save default config: %v", err)
|
||||
return fmt.Errorf("failed to save default config: %w", err)
|
||||
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
|
||||
}
|
||||
|
||||
log.Printf("ResetToDefault: Successfully reset to default config")
|
||||
// 更新元数据
|
||||
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
|
||||
}
|
||||
|
@@ -1,195 +0,0 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
// FileService 提供文件操作
|
||||
type FileService struct{}
|
||||
|
||||
// NewFileService 创建新的文件服务实例
|
||||
func NewFileService() *FileService {
|
||||
return &FileService{}
|
||||
}
|
||||
|
||||
// EnsureDir 确保目录存在,如不存在则创建
|
||||
func (fs *FileService) EnsureDir(dirPath string) error {
|
||||
log.Printf("EnsureDir: Checking directory: %s", dirPath)
|
||||
|
||||
if _, err := os.Stat(dirPath); os.IsNotExist(err) {
|
||||
log.Printf("EnsureDir: Directory does not exist, creating: %s", dirPath)
|
||||
err := os.MkdirAll(dirPath, 0755)
|
||||
if err != nil {
|
||||
log.Printf("EnsureDir: Failed to create directory: %v", err)
|
||||
return err
|
||||
}
|
||||
log.Printf("EnsureDir: Directory created successfully: %s", dirPath)
|
||||
} else {
|
||||
log.Printf("EnsureDir: Directory already exists: %s", dirPath)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetFileModTime 获取文件的修改时间
|
||||
func (fs *FileService) GetFileModTime(filePath string) (time.Time, error) {
|
||||
log.Printf("GetFileModTime: Getting modification time for file: %s", filePath)
|
||||
|
||||
fileInfo, err := os.Stat(filePath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
log.Printf("GetFileModTime: File does not exist: %s", filePath)
|
||||
return time.Time{}, fmt.Errorf("file does not exist: %w", err)
|
||||
}
|
||||
log.Printf("GetFileModTime: Failed to get file info: %v", err)
|
||||
return time.Time{}, fmt.Errorf("failed to get file info: %w", err)
|
||||
}
|
||||
|
||||
modTime := fileInfo.ModTime()
|
||||
log.Printf("GetFileModTime: File modification time: %s", modTime.Format(time.RFC3339))
|
||||
return modTime, nil
|
||||
}
|
||||
|
||||
// SaveJSONWithCheck 保存JSON数据到文件,带并发检查
|
||||
// expectedModTime是期望的文件修改时间,如果为零值则不检查
|
||||
// onConflict是冲突处理函数,如果文件已被修改且此函数不为nil,则调用此函数合并数据
|
||||
func (fs *FileService) SaveJSONWithCheck(filePath string, data interface{}, expectedModTime time.Time, onConflict func(existingData []byte) (interface{}, error)) error {
|
||||
log.Printf("SaveJSONWithCheck: Saving to file with concurrency check: %s", filePath)
|
||||
|
||||
// 检查文件是否存在且已被修改
|
||||
if !expectedModTime.IsZero() && fs.FileExists(filePath) {
|
||||
currentModTime, err := fs.GetFileModTime(filePath)
|
||||
if err == nil {
|
||||
// 如果文件修改时间与预期不符,说明文件已被其他进程修改
|
||||
if !currentModTime.Equal(expectedModTime) {
|
||||
log.Printf("SaveJSONWithCheck: File has been modified since last read. Expected: %s, Current: %s",
|
||||
expectedModTime.Format(time.RFC3339), currentModTime.Format(time.RFC3339))
|
||||
|
||||
// 如果提供了冲突处理函数,尝试解决冲突
|
||||
if onConflict != nil {
|
||||
log.Printf("SaveJSONWithCheck: Attempting to resolve conflict")
|
||||
|
||||
// 读取当前文件内容
|
||||
existingData, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
log.Printf("SaveJSONWithCheck: Failed to read existing file: %v", err)
|
||||
return fmt.Errorf("failed to read existing file for conflict resolution: %w", err)
|
||||
}
|
||||
|
||||
// 调用冲突处理函数合并数据
|
||||
mergedData, err := onConflict(existingData)
|
||||
if err != nil {
|
||||
log.Printf("SaveJSONWithCheck: Conflict resolution failed: %v", err)
|
||||
return fmt.Errorf("conflict resolution failed: %w", err)
|
||||
}
|
||||
|
||||
// 使用合并后的数据继续保存
|
||||
data = mergedData
|
||||
log.Printf("SaveJSONWithCheck: Conflict resolved, proceeding with merged data")
|
||||
} else {
|
||||
// 没有提供冲突处理函数,返回错误
|
||||
return fmt.Errorf("concurrent modification detected")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.Printf("SaveJSONWithCheck: Could not check file modification time: %v", err)
|
||||
// 继续执行,不中断操作
|
||||
}
|
||||
}
|
||||
|
||||
// 确保目录存在
|
||||
dir := filepath.Dir(filePath)
|
||||
if err := fs.EnsureDir(dir); err != nil {
|
||||
log.Printf("SaveJSONWithCheck: Failed to create directory: %v", err)
|
||||
return fmt.Errorf("failed to create directory: %w", err)
|
||||
}
|
||||
|
||||
// 将数据编码为JSON
|
||||
jsonData, err := json.MarshalIndent(data, "", " ")
|
||||
if err != nil {
|
||||
log.Printf("SaveJSONWithCheck: Failed to encode JSON: %v", err)
|
||||
return fmt.Errorf("failed to encode JSON: %w", err)
|
||||
}
|
||||
|
||||
// 先写入临时文件
|
||||
tempFile := filePath + ".tmp"
|
||||
log.Printf("SaveJSONWithCheck: Writing to temporary file: %s", tempFile)
|
||||
if err := os.WriteFile(tempFile, jsonData, 0644); err != nil {
|
||||
log.Printf("SaveJSONWithCheck: Failed to write temporary file: %v", err)
|
||||
return fmt.Errorf("failed to write temporary file: %w", err)
|
||||
}
|
||||
|
||||
// 原子替换原文件
|
||||
log.Printf("SaveJSONWithCheck: Replacing original file with temporary file")
|
||||
if err := os.Rename(tempFile, filePath); err != nil {
|
||||
os.Remove(tempFile) // 清理临时文件
|
||||
log.Printf("SaveJSONWithCheck: Failed to replace file: %v", err)
|
||||
return fmt.Errorf("failed to replace file: %w", err)
|
||||
}
|
||||
|
||||
log.Printf("SaveJSONWithCheck: File saved successfully: %s", filePath)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SaveJSON 保存JSON数据到文件
|
||||
func (fs *FileService) SaveJSON(filePath string, data interface{}) error {
|
||||
// 调用带并发检查的版本,但不提供冲突解决函数(保持原有行为)
|
||||
return fs.SaveJSONWithCheck(filePath, data, time.Time{}, nil)
|
||||
}
|
||||
|
||||
// LoadJSON 从文件加载JSON数据
|
||||
func (fs *FileService) LoadJSON(filePath string, target interface{}) error {
|
||||
log.Printf("LoadJSON: Loading from file: %s", filePath)
|
||||
|
||||
// 检查文件是否存在
|
||||
if _, err := os.Stat(filePath); os.IsNotExist(err) {
|
||||
log.Printf("LoadJSON: File does not exist: %s", filePath)
|
||||
return fmt.Errorf("file does not exist: %w", err)
|
||||
}
|
||||
|
||||
// 读取文件内容
|
||||
data, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
log.Printf("LoadJSON: Failed to read file: %v", err)
|
||||
return fmt.Errorf("failed to read file: %w", err)
|
||||
}
|
||||
|
||||
// 解析JSON数据
|
||||
if err := json.Unmarshal(data, target); err != nil {
|
||||
log.Printf("LoadJSON: Failed to parse JSON: %v", err)
|
||||
return fmt.Errorf("failed to parse JSON: %w", err)
|
||||
}
|
||||
|
||||
log.Printf("LoadJSON: File loaded successfully: %s", filePath)
|
||||
return nil
|
||||
}
|
||||
|
||||
// FileExists 检查文件是否存在
|
||||
func (fs *FileService) FileExists(filePath string) bool {
|
||||
_, err := os.Stat(filePath)
|
||||
exists := !os.IsNotExist(err)
|
||||
log.Printf("FileExists: Checking if file exists: %s, exists: %v", filePath, exists)
|
||||
return exists
|
||||
}
|
||||
|
||||
// DeleteFile 删除文件
|
||||
func (fs *FileService) DeleteFile(filePath string) error {
|
||||
log.Printf("DeleteFile: Deleting file: %s", filePath)
|
||||
|
||||
if !fs.FileExists(filePath) {
|
||||
log.Printf("DeleteFile: File does not exist, nothing to delete: %s", filePath)
|
||||
return nil // 文件不存在视为删除成功
|
||||
}
|
||||
|
||||
err := os.Remove(filePath)
|
||||
if err != nil {
|
||||
log.Printf("DeleteFile: Failed to delete file: %v", err)
|
||||
} else {
|
||||
log.Printf("DeleteFile: File deleted successfully: %s", filePath)
|
||||
}
|
||||
return err
|
||||
}
|
@@ -6,20 +6,15 @@ import (
|
||||
|
||||
// ServiceManager 服务管理器,负责协调各个服务
|
||||
type ServiceManager struct {
|
||||
fileService *FileService
|
||||
configService *ConfigService
|
||||
}
|
||||
|
||||
// NewServiceManager 创建新的服务管理器实例
|
||||
func NewServiceManager() *ServiceManager {
|
||||
// 初始化文件服务
|
||||
fileService := NewFileService()
|
||||
|
||||
// 初始化配置服务
|
||||
configService := NewConfigService(fileService)
|
||||
configService := NewConfigService()
|
||||
|
||||
return &ServiceManager{
|
||||
fileService: fileService,
|
||||
configService: configService,
|
||||
}
|
||||
}
|
||||
@@ -27,16 +22,10 @@ func NewServiceManager() *ServiceManager {
|
||||
// GetServices 获取所有wails服务列表
|
||||
func (sm *ServiceManager) GetServices() []application.Service {
|
||||
return []application.Service{
|
||||
application.NewService(sm.fileService),
|
||||
application.NewService(sm.configService),
|
||||
}
|
||||
}
|
||||
|
||||
// GetFileService 获取文件服务实例
|
||||
func (sm *ServiceManager) GetFileService() *FileService {
|
||||
return sm.fileService
|
||||
}
|
||||
|
||||
// GetConfigService 获取配置服务实例
|
||||
func (sm *ServiceManager) GetConfigService() *ConfigService {
|
||||
return sm.configService
|
||||
|
232
internal/services/store_service.go
Normal file
232
internal/services/store_service.go
Normal file
@@ -0,0 +1,232 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/wailsapp/wails/v3/pkg/services/log"
|
||||
)
|
||||
|
||||
// StoreOption 存储服务配置选项
|
||||
type StoreOption struct {
|
||||
FilePath string // 存储文件路径
|
||||
AutoSave bool // 是否自动保存
|
||||
Logger *log.LoggerService
|
||||
}
|
||||
|
||||
// Store 泛型存储服务
|
||||
type Store[T any] struct {
|
||||
option StoreOption
|
||||
data T
|
||||
dataMap map[string]any
|
||||
unsaved bool
|
||||
lock sync.RWMutex
|
||||
logger *log.LoggerService
|
||||
}
|
||||
|
||||
// NewStore 存储服务
|
||||
func NewStore[T any](option StoreOption) *Store[T] {
|
||||
logger := option.Logger
|
||||
if logger == nil {
|
||||
logger = log.New()
|
||||
}
|
||||
|
||||
// 确保目录存在
|
||||
if option.FilePath != "" {
|
||||
dir := filepath.Dir(option.FilePath)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
logger.Error("store: Failed to create directory", "path", dir, "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
store := &Store[T]{
|
||||
option: option,
|
||||
dataMap: make(map[string]any),
|
||||
logger: logger,
|
||||
}
|
||||
|
||||
// 加载数据
|
||||
if err := store.load(); err != nil {
|
||||
logger.Error("store: Failed to load data", "error", err)
|
||||
}
|
||||
|
||||
return store
|
||||
}
|
||||
|
||||
// load 加载数据
|
||||
func (s *Store[T]) load() error {
|
||||
if s.option.FilePath == "" {
|
||||
return fmt.Errorf("store: FilePath not set")
|
||||
}
|
||||
|
||||
// 如果文件不存在
|
||||
if _, err := os.Stat(s.option.FilePath); os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
|
||||
file, err := os.Open(s.option.FilePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("store: Failed to open file: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
bytes, err := io.ReadAll(file)
|
||||
if err != nil {
|
||||
return fmt.Errorf("store: Failed to read file: %w", err)
|
||||
}
|
||||
|
||||
if len(bytes) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
if err := json.Unmarshal(bytes, &s.data); err != nil {
|
||||
// 尝试加载为map格式
|
||||
if err := json.Unmarshal(bytes, &s.dataMap); err != nil {
|
||||
return fmt.Errorf("store: Failed to parse data: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Save 保存数据
|
||||
func (s *Store[T]) Save() error {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
err := s.saveInternal()
|
||||
if err != nil {
|
||||
s.logger.Error("store: Failed to save", "error", err)
|
||||
return fmt.Errorf("store: Failed to save: %w", err)
|
||||
}
|
||||
|
||||
s.unsaved = false
|
||||
return nil
|
||||
}
|
||||
|
||||
// saveInternal 内部保存实现
|
||||
func (s *Store[T]) saveInternal() error {
|
||||
if s.option.FilePath == "" {
|
||||
return fmt.Errorf("store: FilePath not set")
|
||||
}
|
||||
|
||||
// 创建临时文件
|
||||
dir := filepath.Dir(s.option.FilePath)
|
||||
tempFile, err := os.CreateTemp(dir, "store-*.tmp")
|
||||
if err != nil {
|
||||
return fmt.Errorf("store: Failed to create temp file: %w", err)
|
||||
}
|
||||
tempFilePath := tempFile.Name()
|
||||
defer func() {
|
||||
tempFile.Close()
|
||||
// 如果出错,删除临时文件
|
||||
if err != nil {
|
||||
os.Remove(tempFilePath)
|
||||
}
|
||||
}()
|
||||
|
||||
// 序列化数据
|
||||
bytes, err := json.MarshalIndent(s.data, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("store: Failed to serialize data: %w", err)
|
||||
}
|
||||
|
||||
// 写入临时文件
|
||||
if _, err = tempFile.Write(bytes); err != nil {
|
||||
return fmt.Errorf("store: Failed to write temp file: %w", err)
|
||||
}
|
||||
|
||||
// 确保所有数据已写入磁盘
|
||||
if err = tempFile.Sync(); err != nil {
|
||||
return fmt.Errorf("store: Failed to sync file: %w", err)
|
||||
}
|
||||
|
||||
// 关闭临时文件
|
||||
if err = tempFile.Close(); err != nil {
|
||||
return fmt.Errorf("store: Failed to close temp file: %w", err)
|
||||
}
|
||||
|
||||
// 原子替换文件
|
||||
if err = os.Rename(tempFilePath, s.option.FilePath); err != nil {
|
||||
return fmt.Errorf("store: Failed to rename file: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get 获取数据
|
||||
func (s *Store[T]) Get() T {
|
||||
s.lock.RLock()
|
||||
defer s.lock.RUnlock()
|
||||
return s.data
|
||||
}
|
||||
|
||||
// GetProperty 获取指定属性
|
||||
func (s *Store[T]) GetProperty(key string) any {
|
||||
s.lock.RLock()
|
||||
defer s.lock.RUnlock()
|
||||
|
||||
if key == "" {
|
||||
return s.dataMap
|
||||
}
|
||||
return s.dataMap[key]
|
||||
}
|
||||
|
||||
// Set 设置数据
|
||||
func (s *Store[T]) Set(data T) error {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
s.data = data
|
||||
s.unsaved = true
|
||||
|
||||
if s.option.AutoSave {
|
||||
return s.saveInternal()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetProperty 设置指定属性
|
||||
func (s *Store[T]) SetProperty(key string, value any) error {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
s.dataMap[key] = value
|
||||
s.unsaved = true
|
||||
|
||||
if s.option.AutoSave {
|
||||
return s.saveInternal()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete 删除指定属性
|
||||
func (s *Store[T]) Delete(key string) error {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
|
||||
delete(s.dataMap, key)
|
||||
s.unsaved = true
|
||||
|
||||
if s.option.AutoSave {
|
||||
return s.saveInternal()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// HasUnsavedChanges 是否有未保存的更改
|
||||
func (s *Store[T]) HasUnsavedChanges() bool {
|
||||
s.lock.RLock()
|
||||
defer s.lock.RUnlock()
|
||||
return s.unsaved
|
||||
}
|
Reference in New Issue
Block a user