♻️ Refactor backup service complete.
Some checks failed
CodeQL Advanced / Analyze (go) (push) Has been cancelled
CodeQL Advanced / Analyze (actions) (push) Has been cancelled
CodeQL Advanced / Analyze (c-cpp) (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
CodeQL Advanced / Analyze (rust) (push) Has been cancelled
Some checks failed
CodeQL Advanced / Analyze (go) (push) Has been cancelled
CodeQL Advanced / Analyze (actions) (push) Has been cancelled
CodeQL Advanced / Analyze (c-cpp) (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
CodeQL Advanced / Analyze (rust) (push) Has been cancelled
This commit is contained in:
@@ -61,5 +61,3 @@ export class ServiceOptions {
|
|||||||
return new ServiceOptions($$parsedSource as Partial<ServiceOptions>);
|
return new ServiceOptions($$parsedSource as Partial<ServiceOptions>);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Window = any;
|
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
export * from "./models.js";
|
||||||
17
frontend/bindings/voidraft/internal/common/helper/models.ts
Normal file
17
frontend/bindings/voidraft/internal/common/helper/models.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore: Unused imports
|
||||||
|
import {Create as $Create} from "@wailsio/runtime";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CancelFunc 取消订阅函数
|
||||||
|
* 调用此函数可以取消对配置的监听
|
||||||
|
*/
|
||||||
|
export type CancelFunc = any;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ObserverCallback 观察者回调函数
|
||||||
|
*/
|
||||||
|
export type ObserverCallback = any;
|
||||||
@@ -21,7 +21,7 @@ export class Document {
|
|||||||
/**
|
/**
|
||||||
* UUID for cross-device sync (UUIDv7)
|
* UUID for cross-device sync (UUIDv7)
|
||||||
*/
|
*/
|
||||||
"uuid": string;
|
"uuid": string | null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* creation time
|
* creation time
|
||||||
@@ -56,7 +56,7 @@ export class Document {
|
|||||||
/** Creates a new Document instance. */
|
/** Creates a new Document instance. */
|
||||||
constructor($$source: Partial<Document> = {}) {
|
constructor($$source: Partial<Document> = {}) {
|
||||||
if (!("uuid" in $$source)) {
|
if (!("uuid" in $$source)) {
|
||||||
this["uuid"] = "";
|
this["uuid"] = null;
|
||||||
}
|
}
|
||||||
if (!("created_at" in $$source)) {
|
if (!("created_at" in $$source)) {
|
||||||
this["created_at"] = "";
|
this["created_at"] = "";
|
||||||
@@ -98,7 +98,7 @@ export class Extension {
|
|||||||
/**
|
/**
|
||||||
* UUID for cross-device sync (UUIDv7)
|
* UUID for cross-device sync (UUIDv7)
|
||||||
*/
|
*/
|
||||||
"uuid": string;
|
"uuid": string | null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* creation time
|
* creation time
|
||||||
@@ -133,7 +133,7 @@ export class Extension {
|
|||||||
/** Creates a new Extension instance. */
|
/** Creates a new Extension instance. */
|
||||||
constructor($$source: Partial<Extension> = {}) {
|
constructor($$source: Partial<Extension> = {}) {
|
||||||
if (!("uuid" in $$source)) {
|
if (!("uuid" in $$source)) {
|
||||||
this["uuid"] = "";
|
this["uuid"] = null;
|
||||||
}
|
}
|
||||||
if (!("created_at" in $$source)) {
|
if (!("created_at" in $$source)) {
|
||||||
this["created_at"] = "";
|
this["created_at"] = "";
|
||||||
@@ -179,7 +179,7 @@ export class KeyBinding {
|
|||||||
/**
|
/**
|
||||||
* UUID for cross-device sync (UUIDv7)
|
* UUID for cross-device sync (UUIDv7)
|
||||||
*/
|
*/
|
||||||
"uuid": string;
|
"uuid": string | null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* creation time
|
* creation time
|
||||||
@@ -219,7 +219,7 @@ export class KeyBinding {
|
|||||||
/** Creates a new KeyBinding instance. */
|
/** Creates a new KeyBinding instance. */
|
||||||
constructor($$source: Partial<KeyBinding> = {}) {
|
constructor($$source: Partial<KeyBinding> = {}) {
|
||||||
if (!("uuid" in $$source)) {
|
if (!("uuid" in $$source)) {
|
||||||
this["uuid"] = "";
|
this["uuid"] = null;
|
||||||
}
|
}
|
||||||
if (!("created_at" in $$source)) {
|
if (!("created_at" in $$source)) {
|
||||||
this["created_at"] = "";
|
this["created_at"] = "";
|
||||||
@@ -261,7 +261,7 @@ export class Theme {
|
|||||||
/**
|
/**
|
||||||
* UUID for cross-device sync (UUIDv7)
|
* UUID for cross-device sync (UUIDv7)
|
||||||
*/
|
*/
|
||||||
"uuid": string;
|
"uuid": string | null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* creation time
|
* creation time
|
||||||
@@ -296,7 +296,7 @@ export class Theme {
|
|||||||
/** Creates a new Theme instance. */
|
/** Creates a new Theme instance. */
|
||||||
constructor($$source: Partial<Theme> = {}) {
|
constructor($$source: Partial<Theme> = {}) {
|
||||||
if (!("uuid" in $$source)) {
|
if (!("uuid" in $$source)) {
|
||||||
this["uuid"] = "";
|
this["uuid"] = null;
|
||||||
}
|
}
|
||||||
if (!("created_at" in $$source)) {
|
if (!("created_at" in $$source)) {
|
||||||
this["created_at"] = "";
|
this["created_at"] = "";
|
||||||
|
|||||||
@@ -15,11 +15,10 @@ import {Call as $Call, Create as $Create} from "@wailsio/runtime";
|
|||||||
import * as application$0 from "../../../github.com/wailsapp/wails/v3/pkg/application/models.js";
|
import * as application$0 from "../../../github.com/wailsapp/wails/v3/pkg/application/models.js";
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore: Unused imports
|
// @ts-ignore: Unused imports
|
||||||
import * as models$0 from "../models/models.js";
|
import * as helper$0 from "../common/helper/models.js";
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore: Unused imports
|
// @ts-ignore: Unused imports
|
||||||
import * as $models from "./models.js";
|
import * as models$0 from "../models/models.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get 获取配置项
|
* Get 获取配置项
|
||||||
@@ -50,7 +49,7 @@ export function MigrateConfig(): Promise<void> & { cancel(): void } {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ResetConfig 强制重置所有配置为默认值
|
* ResetConfig 重置所有配置为默认值
|
||||||
*/
|
*/
|
||||||
export function ResetConfig(): Promise<void> & { cancel(): void } {
|
export function ResetConfig(): Promise<void> & { cancel(): void } {
|
||||||
let $resultPromise = $Call.ByID(3593047389) as any;
|
let $resultPromise = $Call.ByID(3593047389) as any;
|
||||||
@@ -66,7 +65,7 @@ export function ServiceShutdown(): Promise<void> & { cancel(): void } {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ServiceStartup initializes the service when the application starts
|
* ServiceStartup 服务启动时初始化
|
||||||
*/
|
*/
|
||||||
export function ServiceStartup(options: application$0.ServiceOptions): Promise<void> & { cancel(): void } {
|
export function ServiceStartup(options: application$0.ServiceOptions): Promise<void> & { cancel(): void } {
|
||||||
let $resultPromise = $Call.ByID(3311949428, options) as any;
|
let $resultPromise = $Call.ByID(3311949428, options) as any;
|
||||||
@@ -84,7 +83,7 @@ export function Set(key: string, value: any): Promise<void> & { cancel(): void }
|
|||||||
/**
|
/**
|
||||||
* Watch 注册配置变更监听器
|
* Watch 注册配置变更监听器
|
||||||
*/
|
*/
|
||||||
export function Watch(path: string, callback: $models.ObserverCallback): Promise<$models.CancelFunc> & { cancel(): void } {
|
export function Watch(path: string, callback: helper$0.ObserverCallback): Promise<helper$0.CancelFunc> & { cancel(): void } {
|
||||||
let $resultPromise = $Call.ByID(1143583035, path, callback) as any;
|
let $resultPromise = $Call.ByID(1143583035, path, callback) as any;
|
||||||
return $resultPromise;
|
return $resultPromise;
|
||||||
}
|
}
|
||||||
@@ -92,7 +91,7 @@ export function Watch(path: string, callback: $models.ObserverCallback): Promise
|
|||||||
/**
|
/**
|
||||||
* WatchWithContext 使用 Context 注册监听器
|
* WatchWithContext 使用 Context 注册监听器
|
||||||
*/
|
*/
|
||||||
export function WatchWithContext(path: string, callback: $models.ObserverCallback): Promise<void> & { cancel(): void } {
|
export function WatchWithContext(path: string, callback: helper$0.ObserverCallback): Promise<void> & { cancel(): void } {
|
||||||
let $resultPromise = $Call.ByID(1454973098, path, callback) as any;
|
let $resultPromise = $Call.ByID(1454973098, path, callback) as any;
|
||||||
return $resultPromise;
|
return $resultPromise;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,12 +18,12 @@ import * as application$0 from "../../../github.com/wailsapp/wails/v3/pkg/applic
|
|||||||
import * as models$0 from "../models/models.js";
|
import * as models$0 from "../models/models.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GetCurrentHotkey 获取当前热键
|
* GetSupportedKeys 返回系统支持的快捷键列表
|
||||||
*/
|
*/
|
||||||
export function GetCurrentHotkey(): Promise<models$0.HotkeyCombo | null> & { cancel(): void } {
|
export function GetSupportedKeys(): Promise<string[]> & { cancel(): void } {
|
||||||
let $resultPromise = $Call.ByID(2572811187) as any;
|
let $resultPromise = $Call.ByID(1511528650) as any;
|
||||||
let $typingPromise = $resultPromise.then(($result: any) => {
|
let $typingPromise = $resultPromise.then(($result: any) => {
|
||||||
return $$createType1($result);
|
return $$createType0($result);
|
||||||
}) as any;
|
}) as any;
|
||||||
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
|
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
|
||||||
return $typingPromise;
|
return $typingPromise;
|
||||||
@@ -86,5 +86,4 @@ export function UpdateHotkey(enable: boolean, combo: models$0.HotkeyCombo | null
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Private type creation functions
|
// Private type creation functions
|
||||||
const $$createType0 = models$0.HotkeyCombo.createFrom;
|
const $$createType0 = $Create.Array($Create.Any);
|
||||||
const $$createType1 = $Create.Nullable($$createType0);
|
|
||||||
|
|||||||
@@ -12,12 +12,6 @@ import * as http$0 from "../../../net/http/models.js";
|
|||||||
// @ts-ignore: Unused imports
|
// @ts-ignore: Unused imports
|
||||||
import * as time$0 from "../../../time/models.js";
|
import * as time$0 from "../../../time/models.js";
|
||||||
|
|
||||||
/**
|
|
||||||
* CancelFunc 取消订阅函数
|
|
||||||
* 调用此函数可以取消对配置的监听
|
|
||||||
*/
|
|
||||||
export type CancelFunc = any;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HttpRequest HTTP请求结构
|
* HttpRequest HTTP请求结构
|
||||||
*/
|
*/
|
||||||
@@ -251,11 +245,6 @@ export class OSInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* ObserverCallback 观察者回调函数
|
|
||||||
*/
|
|
||||||
export type ObserverCallback = any;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SelfUpdateResult 自我更新结果
|
* SelfUpdateResult 自我更新结果
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -14,18 +14,6 @@ import {Call as $Call, Create as $Create} from "@wailsio/runtime";
|
|||||||
// @ts-ignore: Unused imports
|
// @ts-ignore: Unused imports
|
||||||
import * as application$0 from "../../../github.com/wailsapp/wails/v3/pkg/application/models.js";
|
import * as application$0 from "../../../github.com/wailsapp/wails/v3/pkg/application/models.js";
|
||||||
|
|
||||||
/**
|
|
||||||
* GetOpenWindows 获取所有打开的文档窗口
|
|
||||||
*/
|
|
||||||
export function GetOpenWindows(): Promise<application$0.Window[]> & { cancel(): void } {
|
|
||||||
let $resultPromise = $Call.ByID(1464997251) as any;
|
|
||||||
let $typingPromise = $resultPromise.then(($result: any) => {
|
|
||||||
return $$createType0($result);
|
|
||||||
}) as any;
|
|
||||||
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
|
|
||||||
return $typingPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IsDocumentWindowOpen 检查指定文档的窗口是否已打开
|
* IsDocumentWindowOpen 检查指定文档的窗口是否已打开
|
||||||
*/
|
*/
|
||||||
@@ -57,6 +45,3 @@ export function ServiceStartup(options: application$0.ServiceOptions): Promise<v
|
|||||||
let $resultPromise = $Call.ByID(2432987694, options) as any;
|
let $resultPromise = $Call.ByID(2432987694, options) as any;
|
||||||
return $resultPromise;
|
return $resultPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Private type creation functions
|
|
||||||
const $$createType0 = $Create.Array($Create.Any);
|
|
||||||
|
|||||||
379
frontend/package-lock.json
generated
379
frontend/package-lock.json
generated
@@ -71,7 +71,7 @@
|
|||||||
"@lezer/generator": "^1.8.0",
|
"@lezer/generator": "^1.8.0",
|
||||||
"@types/node": "^24.10.1",
|
"@types/node": "^24.10.1",
|
||||||
"@vitejs/plugin-vue": "^6.0.2",
|
"@vitejs/plugin-vue": "^6.0.2",
|
||||||
"@wailsio/runtime": "*",
|
"@wailsio/runtime": "latest",
|
||||||
"cross-env": "^10.1.0",
|
"cross-env": "^10.1.0",
|
||||||
"eslint": "^9.39.1",
|
"eslint": "^9.39.1",
|
||||||
"eslint-plugin-vue": "^10.6.2",
|
"eslint-plugin-vue": "^10.6.2",
|
||||||
@@ -142,7 +142,6 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.28.5.tgz",
|
"resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.28.5.tgz",
|
||||||
"integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
|
"integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/types": "^7.28.5"
|
"@babel/types": "^7.28.5"
|
||||||
},
|
},
|
||||||
@@ -216,7 +215,6 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/@codemirror/autocomplete/-/autocomplete-6.20.0.tgz",
|
"resolved": "https://registry.npmmirror.com/@codemirror/autocomplete/-/autocomplete-6.20.0.tgz",
|
||||||
"integrity": "sha512-bOwvTOIJcG5FVo5gUUupiwYh8MioPLQ4UcqbcRf7UQ98X90tCa9E1kZ3Z7tqwpZxYyOvh1YTYbmZE9RTfTp5hg==",
|
"integrity": "sha512-bOwvTOIJcG5FVo5gUUupiwYh8MioPLQ4UcqbcRf7UQ98X90tCa9E1kZ3Z7tqwpZxYyOvh1YTYbmZE9RTfTp5hg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/language": "^6.0.0",
|
"@codemirror/language": "^6.0.0",
|
||||||
"@codemirror/state": "^6.0.0",
|
"@codemirror/state": "^6.0.0",
|
||||||
@@ -265,7 +263,6 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/@codemirror/lang-css/-/lang-css-6.3.1.tgz",
|
"resolved": "https://registry.npmmirror.com/@codemirror/lang-css/-/lang-css-6.3.1.tgz",
|
||||||
"integrity": "sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==",
|
"integrity": "sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/autocomplete": "^6.0.0",
|
"@codemirror/autocomplete": "^6.0.0",
|
||||||
"@codemirror/language": "^6.0.0",
|
"@codemirror/language": "^6.0.0",
|
||||||
@@ -292,7 +289,6 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/@codemirror/lang-html/-/lang-html-6.4.11.tgz",
|
"resolved": "https://registry.npmmirror.com/@codemirror/lang-html/-/lang-html-6.4.11.tgz",
|
||||||
"integrity": "sha512-9NsXp7Nwp891pQchI7gPdTwBuSuT3K65NGTHWHNJ55HjYcHLllr0rbIZNdOzas9ztc1EUVBlHou85FFZS4BNnw==",
|
"integrity": "sha512-9NsXp7Nwp891pQchI7gPdTwBuSuT3K65NGTHWHNJ55HjYcHLllr0rbIZNdOzas9ztc1EUVBlHou85FFZS4BNnw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/autocomplete": "^6.0.0",
|
"@codemirror/autocomplete": "^6.0.0",
|
||||||
"@codemirror/lang-css": "^6.0.0",
|
"@codemirror/lang-css": "^6.0.0",
|
||||||
@@ -320,7 +316,6 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/@codemirror/lang-javascript/-/lang-javascript-6.2.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@codemirror/lang-javascript/-/lang-javascript-6.2.4.tgz",
|
||||||
"integrity": "sha512-0WVmhp1QOqZ4Rt6GlVGwKJN3KW7Xh4H2q8ZZNGZaP6lRdxXJzmjm4FqvmOojVj6khWJHIb9sp7U/72W7xQgqAA==",
|
"integrity": "sha512-0WVmhp1QOqZ4Rt6GlVGwKJN3KW7Xh4H2q8ZZNGZaP6lRdxXJzmjm4FqvmOojVj6khWJHIb9sp7U/72W7xQgqAA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/autocomplete": "^6.0.0",
|
"@codemirror/autocomplete": "^6.0.0",
|
||||||
"@codemirror/language": "^6.6.0",
|
"@codemirror/language": "^6.6.0",
|
||||||
@@ -533,7 +528,6 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/@codemirror/language/-/language-6.11.3.tgz",
|
"resolved": "https://registry.npmmirror.com/@codemirror/language/-/language-6.11.3.tgz",
|
||||||
"integrity": "sha512-9HBM2XnwDj7fnu0551HkGdrUrrqmYq/WC5iv6nbY2WdicXdGbhR/gfbZOH73Aqj4351alY1+aoG9rCNfiwS1RA==",
|
"integrity": "sha512-9HBM2XnwDj7fnu0551HkGdrUrrqmYq/WC5iv6nbY2WdicXdGbhR/gfbZOH73Aqj4351alY1+aoG9rCNfiwS1RA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/state": "^6.0.0",
|
"@codemirror/state": "^6.0.0",
|
||||||
"@codemirror/view": "^6.23.0",
|
"@codemirror/view": "^6.23.0",
|
||||||
@@ -610,7 +604,6 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/@codemirror/state/-/state-6.5.2.tgz",
|
"resolved": "https://registry.npmmirror.com/@codemirror/state/-/state-6.5.2.tgz",
|
||||||
"integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==",
|
"integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@marijn/find-cluster-break": "^1.0.0"
|
"@marijn/find-cluster-break": "^1.0.0"
|
||||||
}
|
}
|
||||||
@@ -620,7 +613,6 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/@codemirror/view/-/view-6.38.8.tgz",
|
"resolved": "https://registry.npmmirror.com/@codemirror/view/-/view-6.38.8.tgz",
|
||||||
"integrity": "sha512-XcE9fcnkHCbWkjeKyi0lllwXmBLtyYb5dt89dJyx23I9+LSh5vZDIuk7OLG4VM1lgrXZQcY6cxyZyk5WVPRv/A==",
|
"integrity": "sha512-XcE9fcnkHCbWkjeKyi0lllwXmBLtyYb5dt89dJyx23I9+LSh5vZDIuk7OLG4VM1lgrXZQcY6cxyZyk5WVPRv/A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/state": "^6.5.0",
|
"@codemirror/state": "^6.5.0",
|
||||||
"crelt": "^1.0.6",
|
"crelt": "^1.0.6",
|
||||||
@@ -1395,7 +1387,7 @@
|
|||||||
"version": "0.3.13",
|
"version": "0.3.13",
|
||||||
"resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
|
"resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
|
||||||
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
|
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/sourcemap-codec": "^1.5.0",
|
"@jridgewell/sourcemap-codec": "^1.5.0",
|
||||||
@@ -1406,7 +1398,7 @@
|
|||||||
"version": "2.3.5",
|
"version": "2.3.5",
|
||||||
"resolved": "https://registry.npmmirror.com/@jridgewell/remapping/-/remapping-2.3.5.tgz",
|
"resolved": "https://registry.npmmirror.com/@jridgewell/remapping/-/remapping-2.3.5.tgz",
|
||||||
"integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
|
"integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/gen-mapping": "^0.3.5",
|
"@jridgewell/gen-mapping": "^0.3.5",
|
||||||
@@ -1417,7 +1409,7 @@
|
|||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
"resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
||||||
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
@@ -1433,7 +1425,7 @@
|
|||||||
"version": "0.3.31",
|
"version": "0.3.31",
|
||||||
"resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
|
"resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
|
||||||
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
|
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/resolve-uri": "^3.1.0",
|
"@jridgewell/resolve-uri": "^3.1.0",
|
||||||
@@ -1444,8 +1436,7 @@
|
|||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@lezer/common/-/common-1.3.0.tgz",
|
"resolved": "https://registry.npmmirror.com/@lezer/common/-/common-1.3.0.tgz",
|
||||||
"integrity": "sha512-L9X8uHCYU310o99L3/MpJKYxPzXPOS7S0NmBaM7UO/x2Kb2WbmMLSkfvdr1KxRIFYOpbY0Jhn7CfLSUDzL8arQ==",
|
"integrity": "sha512-L9X8uHCYU310o99L3/MpJKYxPzXPOS7S0NmBaM7UO/x2Kb2WbmMLSkfvdr1KxRIFYOpbY0Jhn7CfLSUDzL8arQ==",
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/@lezer/cpp": {
|
"node_modules/@lezer/cpp": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
@@ -1499,7 +1490,6 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/@lezer/highlight/-/highlight-1.2.3.tgz",
|
"resolved": "https://registry.npmmirror.com/@lezer/highlight/-/highlight-1.2.3.tgz",
|
||||||
"integrity": "sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==",
|
"integrity": "sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lezer/common": "^1.3.0"
|
"@lezer/common": "^1.3.0"
|
||||||
}
|
}
|
||||||
@@ -1531,7 +1521,6 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/@lezer/javascript/-/javascript-1.5.1.tgz",
|
"resolved": "https://registry.npmmirror.com/@lezer/javascript/-/javascript-1.5.1.tgz",
|
||||||
"integrity": "sha512-ATOImjeVJuvgm3JQ/bpo2Tmv55HSScE2MTPnKRMRIPx2cLhHGyX2VnqpHhtIV1tVzIjZDbcWQm+NCTF40ggZVw==",
|
"integrity": "sha512-ATOImjeVJuvgm3JQ/bpo2Tmv55HSScE2MTPnKRMRIPx2cLhHGyX2VnqpHhtIV1tVzIjZDbcWQm+NCTF40ggZVw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lezer/common": "^1.2.0",
|
"@lezer/common": "^1.2.0",
|
||||||
"@lezer/highlight": "^1.1.3",
|
"@lezer/highlight": "^1.1.3",
|
||||||
@@ -1564,7 +1553,6 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/@lezer/lr/-/lr-1.4.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@lezer/lr/-/lr-1.4.4.tgz",
|
||||||
"integrity": "sha512-LHL17Mq0OcFXm1pGQssuGTQFPPdxARjKM8f7GA5+sGtHi0K3R84YaSbmche0+RKWHnCsx9asEe5OWOI4FHfe4A==",
|
"integrity": "sha512-LHL17Mq0OcFXm1pGQssuGTQFPPdxARjKM8f7GA5+sGtHi0K3R84YaSbmche0+RKWHnCsx9asEe5OWOI4FHfe4A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lezer/common": "^1.0.0"
|
"@lezer/common": "^1.0.0"
|
||||||
}
|
}
|
||||||
@@ -2773,7 +2761,7 @@
|
|||||||
"version": "1.0.8",
|
"version": "1.0.8",
|
||||||
"resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz",
|
"resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz",
|
||||||
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
|
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/geojson": {
|
"node_modules/@types/geojson": {
|
||||||
@@ -2846,7 +2834,6 @@
|
|||||||
"integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
|
"integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~7.16.0"
|
"undici-types": "~7.16.0"
|
||||||
}
|
}
|
||||||
@@ -2925,7 +2912,6 @@
|
|||||||
"integrity": "sha512-jCzKdm/QK0Kg4V4IK/oMlRZlY+QOcdjv89U2NgKHZk1CYTj82/RVSx1mV/0gqCVMJ/DA+Zf/S4NBWNF8GQ+eqQ==",
|
"integrity": "sha512-jCzKdm/QK0Kg4V4IK/oMlRZlY+QOcdjv89U2NgKHZk1CYTj82/RVSx1mV/0gqCVMJ/DA+Zf/S4NBWNF8GQ+eqQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@typescript-eslint/scope-manager": "8.48.0",
|
"@typescript-eslint/scope-manager": "8.48.0",
|
||||||
"@typescript-eslint/types": "8.48.0",
|
"@typescript-eslint/types": "8.48.0",
|
||||||
@@ -3619,7 +3605,6 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.15.0.tgz",
|
"resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.15.0.tgz",
|
||||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
},
|
},
|
||||||
@@ -4008,35 +3993,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/c12": {
|
|
||||||
"version": "3.0.4",
|
|
||||||
"resolved": "https://registry.npmmirror.com/c12/-/c12-3.0.4.tgz",
|
|
||||||
"integrity": "sha512-t5FaZTYbbCtvxuZq9xxIruYydrAGsJ+8UdP0pZzMiK2xl/gNiSOy0OxhLzHUEEb0m1QXYqfzfvyIFEmz/g9lqg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
|
||||||
"chokidar": "^4.0.3",
|
|
||||||
"confbox": "^0.2.2",
|
|
||||||
"defu": "^6.1.4",
|
|
||||||
"dotenv": "^16.5.0",
|
|
||||||
"exsolve": "^1.0.5",
|
|
||||||
"giget": "^2.0.0",
|
|
||||||
"jiti": "^2.4.2",
|
|
||||||
"ohash": "^2.0.11",
|
|
||||||
"pathe": "^2.0.3",
|
|
||||||
"perfect-debounce": "^1.0.0",
|
|
||||||
"pkg-types": "^2.1.0",
|
|
||||||
"rc9": "^2.1.2"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"magicast": "^0.3.5"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"magicast": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/call-bind": {
|
"node_modules/call-bind": {
|
||||||
"version": "1.0.8",
|
"version": "1.0.8",
|
||||||
"resolved": "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.8.tgz",
|
"resolved": "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.8.tgz",
|
||||||
@@ -4162,7 +4118,6 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/chevrotain/-/chevrotain-11.0.3.tgz",
|
"resolved": "https://registry.npmmirror.com/chevrotain/-/chevrotain-11.0.3.tgz",
|
||||||
"integrity": "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==",
|
"integrity": "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@chevrotain/cst-dts-gen": "11.0.3",
|
"@chevrotain/cst-dts-gen": "11.0.3",
|
||||||
"@chevrotain/gast": "11.0.3",
|
"@chevrotain/gast": "11.0.3",
|
||||||
@@ -4213,16 +4168,6 @@
|
|||||||
"node": ">= 0.10"
|
"node": ">= 0.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/citty": {
|
|
||||||
"version": "0.1.6",
|
|
||||||
"resolved": "https://registry.npmmirror.com/citty/-/citty-0.1.6.tgz",
|
|
||||||
"integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
|
||||||
"consola": "^3.2.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/codemirror": {
|
"node_modules/codemirror": {
|
||||||
"version": "6.0.2",
|
"version": "6.0.2",
|
||||||
"resolved": "https://registry.npmmirror.com/codemirror/-/codemirror-6.0.2.tgz",
|
"resolved": "https://registry.npmmirror.com/codemirror/-/codemirror-6.0.2.tgz",
|
||||||
@@ -4324,16 +4269,6 @@
|
|||||||
"integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==",
|
"integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/consola": {
|
|
||||||
"version": "3.4.2",
|
|
||||||
"resolved": "https://registry.npmmirror.com/consola/-/consola-3.4.2.tgz",
|
|
||||||
"integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"engines": {
|
|
||||||
"node": "^14.18.0 || >=16.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/console-browserify": {
|
"node_modules/console-browserify": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmmirror.com/console-browserify/-/console-browserify-1.2.0.tgz",
|
"resolved": "https://registry.npmmirror.com/console-browserify/-/console-browserify-1.2.0.tgz",
|
||||||
@@ -4522,7 +4457,6 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/cytoscape/-/cytoscape-3.33.1.tgz",
|
"resolved": "https://registry.npmmirror.com/cytoscape/-/cytoscape-3.33.1.tgz",
|
||||||
"integrity": "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==",
|
"integrity": "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10"
|
"node": ">=0.10"
|
||||||
}
|
}
|
||||||
@@ -4932,7 +4866,6 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/d3-selection/-/d3-selection-3.0.0.tgz",
|
"resolved": "https://registry.npmmirror.com/d3-selection/-/d3-selection-3.0.0.tgz",
|
||||||
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
|
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
@@ -5129,13 +5062,6 @@
|
|||||||
"minimalistic-assert": "^1.0.0"
|
"minimalistic-assert": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"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",
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"node_modules/detect-libc": {
|
"node_modules/detect-libc": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-1.0.3.tgz",
|
"resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-1.0.3.tgz",
|
||||||
@@ -5204,19 +5130,6 @@
|
|||||||
"@types/trusted-types": "^2.0.7"
|
"@types/trusted-types": "^2.0.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/dotenv": {
|
|
||||||
"version": "16.5.0",
|
|
||||||
"resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-16.5.0.tgz",
|
|
||||||
"integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==",
|
|
||||||
"license": "BSD-2-Clause",
|
|
||||||
"optional": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://dotenvx.com"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/dunder-proto": {
|
"node_modules/dunder-proto": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
"resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||||
@@ -5267,13 +5180,6 @@
|
|||||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/errx": {
|
|
||||||
"version": "0.1.0",
|
|
||||||
"resolved": "https://registry.npmmirror.com/errx/-/errx-0.1.0.tgz",
|
|
||||||
"integrity": "sha512-fZmsRiDNv07K6s2KkKFTiD2aIvECa7++PKyD5NC32tpRw46qZA3sOz+aM+/V9V0GDHxVTKLziveV4JhzBHDp9Q==",
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"node_modules/es-define-property": {
|
"node_modules/es-define-property": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz",
|
"resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz",
|
||||||
@@ -5374,7 +5280,6 @@
|
|||||||
"integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==",
|
"integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.8.0",
|
"@eslint-community/eslint-utils": "^4.8.0",
|
||||||
"@eslint-community/regexpp": "^4.12.1",
|
"@eslint-community/regexpp": "^4.12.1",
|
||||||
@@ -5689,7 +5594,6 @@
|
|||||||
"integrity": "sha512-v/Z8bvMCajtx4mEXmOo7QEsIzlIOqRXTIwgUfsFOF9gEsespdbD0AkPIka1bSXZ8Y8oZ+2IVDQZePkTfEHZl7Q==",
|
"integrity": "sha512-v/Z8bvMCajtx4mEXmOo7QEsIzlIOqRXTIwgUfsFOF9gEsespdbD0AkPIka1bSXZ8Y8oZ+2IVDQZePkTfEHZl7Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tabbable": "^6.3.0"
|
"tabbable": "^6.3.0"
|
||||||
}
|
}
|
||||||
@@ -5774,24 +5678,6 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/giget": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmmirror.com/giget/-/giget-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
|
||||||
"citty": "^0.1.6",
|
|
||||||
"consola": "^3.4.0",
|
|
||||||
"defu": "^6.1.4",
|
|
||||||
"node-fetch-native": "^1.6.6",
|
|
||||||
"nypm": "^0.6.0",
|
|
||||||
"pathe": "^2.0.3"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"giget": "dist/cli.mjs"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/glob-parent": {
|
"node_modules/glob-parent": {
|
||||||
"version": "6.0.2",
|
"version": "6.0.2",
|
||||||
"resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz",
|
"resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz",
|
||||||
@@ -6351,19 +6237,14 @@
|
|||||||
"version": "2.4.2",
|
"version": "2.4.2",
|
||||||
"resolved": "https://registry.npmmirror.com/jiti/-/jiti-2.4.2.tgz",
|
"resolved": "https://registry.npmmirror.com/jiti/-/jiti-2.4.2.tgz",
|
||||||
"integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==",
|
"integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"jiti": "lib/jiti-cli.mjs"
|
"jiti": "lib/jiti-cli.mjs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/js-tokens": {
|
|
||||||
"version": "9.0.1",
|
|
||||||
"resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-9.0.1.tgz",
|
|
||||||
"integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"node_modules/js-yaml": {
|
"node_modules/js-yaml": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
|
||||||
@@ -6429,23 +6310,6 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/khroma/-/khroma-2.1.0.tgz",
|
"resolved": "https://registry.npmmirror.com/khroma/-/khroma-2.1.0.tgz",
|
||||||
"integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw=="
|
"integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw=="
|
||||||
},
|
},
|
||||||
"node_modules/klona": {
|
|
||||||
"version": "2.0.6",
|
|
||||||
"resolved": "https://registry.npmmirror.com/klona/-/klona-2.0.6.tgz",
|
|
||||||
"integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/knitwork": {
|
|
||||||
"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",
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"node_modules/kolorist": {
|
"node_modules/kolorist": {
|
||||||
"version": "1.8.0",
|
"version": "1.8.0",
|
||||||
"resolved": "https://registry.npmmirror.com/kolorist/-/kolorist-1.8.0.tgz",
|
"resolved": "https://registry.npmmirror.com/kolorist/-/kolorist-1.8.0.tgz",
|
||||||
@@ -6916,13 +6780,6 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
"node_modules/node-fetch-native": {
|
|
||||||
"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",
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"node_modules/node-stdlib-browser": {
|
"node_modules/node-stdlib-browser": {
|
||||||
"version": "1.3.1",
|
"version": "1.3.1",
|
||||||
"resolved": "https://registry.npmmirror.com/node-stdlib-browser/-/node-stdlib-browser-1.3.1.tgz",
|
"resolved": "https://registry.npmmirror.com/node-stdlib-browser/-/node-stdlib-browser-1.3.1.tgz",
|
||||||
@@ -6982,26 +6839,6 @@
|
|||||||
"url": "https://github.com/fb55/nth-check?sponsor=1"
|
"url": "https://github.com/fb55/nth-check?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/nypm": {
|
|
||||||
"version": "0.6.0",
|
|
||||||
"resolved": "https://registry.npmmirror.com/nypm/-/nypm-0.6.0.tgz",
|
|
||||||
"integrity": "sha512-mn8wBFV9G9+UFHIrq+pZ2r2zL4aPau/by3kJb3cM7+5tQHMt6HGQB8FDIeKFYp8o0D2pnH6nVsO88N4AmUxIWg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
|
||||||
"citty": "^0.1.6",
|
|
||||||
"consola": "^3.4.0",
|
|
||||||
"pathe": "^2.0.3",
|
|
||||||
"pkg-types": "^2.0.0",
|
|
||||||
"tinyexec": "^0.3.2"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"nypm": "dist/cli.mjs"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "^14.16.0 || >=16.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/object-inspect": {
|
"node_modules/object-inspect": {
|
||||||
"version": "1.13.4",
|
"version": "1.13.4",
|
||||||
"resolved": "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.4.tgz",
|
"resolved": "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.4.tgz",
|
||||||
@@ -7074,13 +6911,6 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/ohash": {
|
|
||||||
"version": "2.0.11",
|
|
||||||
"resolved": "https://registry.npmmirror.com/ohash/-/ohash-2.0.11.tgz",
|
|
||||||
"integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"node_modules/oniguruma-parser": {
|
"node_modules/oniguruma-parser": {
|
||||||
"version": "0.12.1",
|
"version": "0.12.1",
|
||||||
"resolved": "https://registry.npmmirror.com/oniguruma-parser/-/oniguruma-parser-0.12.1.tgz",
|
"resolved": "https://registry.npmmirror.com/oniguruma-parser/-/oniguruma-parser-0.12.1.tgz",
|
||||||
@@ -7335,7 +7165,6 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/pinia/-/pinia-3.0.4.tgz",
|
"resolved": "https://registry.npmmirror.com/pinia/-/pinia-3.0.4.tgz",
|
||||||
"integrity": "sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==",
|
"integrity": "sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/devtools-api": "^7.7.7"
|
"@vue/devtools-api": "^7.7.7"
|
||||||
},
|
},
|
||||||
@@ -7484,7 +7313,6 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/prettier/-/prettier-3.7.2.tgz",
|
"resolved": "https://registry.npmmirror.com/prettier/-/prettier-3.7.2.tgz",
|
||||||
"integrity": "sha512-n3HV2J6QhItCXndGa3oMWvWFAgN1ibnS7R9mt6iokScBOC0Ul9/iZORmU2IWUMcyAQaMPjTlY3uT34TqocUxMA==",
|
"integrity": "sha512-n3HV2J6QhItCXndGa3oMWvWFAgN1ibnS7R9mt6iokScBOC0Ul9/iZORmU2IWUMcyAQaMPjTlY3uT34TqocUxMA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"prettier": "bin/prettier.cjs"
|
"prettier": "bin/prettier.cjs"
|
||||||
},
|
},
|
||||||
@@ -7617,17 +7445,6 @@
|
|||||||
"safe-buffer": "^5.1.0"
|
"safe-buffer": "^5.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rc9": {
|
|
||||||
"version": "2.1.2",
|
|
||||||
"resolved": "https://registry.npmmirror.com/rc9/-/rc9-2.1.2.tgz",
|
|
||||||
"integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
|
||||||
"defu": "^6.1.4",
|
|
||||||
"destr": "^2.0.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/readable-stream": {
|
"node_modules/readable-stream": {
|
||||||
"version": "3.6.2",
|
"version": "3.6.2",
|
||||||
"resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz",
|
"resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||||
@@ -7749,7 +7566,6 @@
|
|||||||
"integrity": "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==",
|
"integrity": "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/estree": "1.0.8"
|
"@types/estree": "1.0.8"
|
||||||
},
|
},
|
||||||
@@ -7852,7 +7668,6 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/sass/-/sass-1.94.2.tgz",
|
"resolved": "https://registry.npmmirror.com/sass/-/sass-1.94.2.tgz",
|
||||||
"integrity": "sha512-N+7WK20/wOr7CzA2snJcUSSNTCzeCGUTFY3OgeQP3mZ1aj9NMQ0mSTXwlrnd89j33zzQJGqIN52GIOmYrfq46A==",
|
"integrity": "sha512-N+7WK20/wOr7CzA2snJcUSSNTCzeCGUTFY3OgeQP3mZ1aj9NMQ0mSTXwlrnd89j33zzQJGqIN52GIOmYrfq46A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chokidar": "^4.0.0",
|
"chokidar": "^4.0.0",
|
||||||
"immutable": "^5.0.2",
|
"immutable": "^5.0.2",
|
||||||
@@ -7868,18 +7683,11 @@
|
|||||||
"@parcel/watcher": "^2.4.1"
|
"@parcel/watcher": "^2.4.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/scule": {
|
|
||||||
"version": "1.3.0",
|
|
||||||
"resolved": "https://registry.npmmirror.com/scule/-/scule-1.3.0.tgz",
|
|
||||||
"integrity": "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==",
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"node_modules/semver": {
|
"node_modules/semver": {
|
||||||
"version": "7.7.2",
|
"version": "7.7.2",
|
||||||
"resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.2.tgz",
|
"resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.2.tgz",
|
||||||
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"bin": {
|
"bin": {
|
||||||
"semver": "bin/semver.js"
|
"semver": "bin/semver.js"
|
||||||
@@ -8121,7 +7929,7 @@
|
|||||||
"version": "3.10.0",
|
"version": "3.10.0",
|
||||||
"resolved": "https://registry.npmmirror.com/std-env/-/std-env-3.10.0.tgz",
|
"resolved": "https://registry.npmmirror.com/std-env/-/std-env-3.10.0.tgz",
|
||||||
"integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==",
|
"integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/stream-browserify": {
|
"node_modules/stream-browserify": {
|
||||||
@@ -8186,19 +7994,6 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/strip-literal": {
|
|
||||||
"version": "3.0.0",
|
|
||||||
"resolved": "https://registry.npmmirror.com/strip-literal/-/strip-literal-3.0.0.tgz",
|
|
||||||
"integrity": "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
|
||||||
"js-tokens": "^9.0.1"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/antfu"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/style-mod": {
|
"node_modules/style-mod": {
|
||||||
"version": "4.1.2",
|
"version": "4.1.2",
|
||||||
"resolved": "https://registry.npmmirror.com/style-mod/-/style-mod-4.1.2.tgz",
|
"resolved": "https://registry.npmmirror.com/style-mod/-/style-mod-4.1.2.tgz",
|
||||||
@@ -8280,14 +8075,14 @@
|
|||||||
"version": "0.3.2",
|
"version": "0.3.2",
|
||||||
"resolved": "https://registry.npmmirror.com/tinyexec/-/tinyexec-0.3.2.tgz",
|
"resolved": "https://registry.npmmirror.com/tinyexec/-/tinyexec-0.3.2.tgz",
|
||||||
"integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==",
|
"integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/tinyglobby": {
|
"node_modules/tinyglobby": {
|
||||||
"version": "0.2.15",
|
"version": "0.2.15",
|
||||||
"resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.15.tgz",
|
"resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.15.tgz",
|
||||||
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
|
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fdir": "^6.5.0",
|
"fdir": "^6.5.0",
|
||||||
@@ -8304,7 +8099,7 @@
|
|||||||
"version": "6.5.0",
|
"version": "6.5.0",
|
||||||
"resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz",
|
"resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz",
|
||||||
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
|
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12.0.0"
|
"node": ">=12.0.0"
|
||||||
@@ -8322,9 +8117,8 @@
|
|||||||
"version": "4.0.3",
|
"version": "4.0.3",
|
||||||
"resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz",
|
"resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz",
|
||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -8444,7 +8238,6 @@
|
|||||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"peer": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
@@ -8483,29 +8276,6 @@
|
|||||||
"integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==",
|
"integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/unctx": {
|
|
||||||
"version": "2.4.1",
|
|
||||||
"resolved": "https://registry.npmmirror.com/unctx/-/unctx-2.4.1.tgz",
|
|
||||||
"integrity": "sha512-AbaYw0Nm4mK4qjhns67C+kgxR2YWiwlDBPzxrN8h8C6VtAdCgditAY5Dezu3IJy4XVqAnbrXt9oQJvsn3fyozg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
|
||||||
"acorn": "^8.14.0",
|
|
||||||
"estree-walker": "^3.0.3",
|
|
||||||
"magic-string": "^0.30.17",
|
|
||||||
"unplugin": "^2.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/unctx/node_modules/estree-walker": {
|
|
||||||
"version": "3.0.3",
|
|
||||||
"resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-3.0.3.tgz",
|
|
||||||
"integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@types/estree": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/undici-types": {
|
"node_modules/undici-types": {
|
||||||
"version": "7.16.0",
|
"version": "7.16.0",
|
||||||
"resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-7.16.0.tgz",
|
"resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-7.16.0.tgz",
|
||||||
@@ -8513,68 +8283,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/unimport": {
|
|
||||||
"version": "5.0.1",
|
|
||||||
"resolved": "https://registry.npmmirror.com/unimport/-/unimport-5.0.1.tgz",
|
|
||||||
"integrity": "sha512-1YWzPj6wYhtwHE+9LxRlyqP4DiRrhGfJxdtH475im8ktyZXO3jHj/3PZ97zDdvkYoovFdi0K4SKl3a7l92v3sQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
|
||||||
"acorn": "^8.14.1",
|
|
||||||
"escape-string-regexp": "^5.0.0",
|
|
||||||
"estree-walker": "^3.0.3",
|
|
||||||
"local-pkg": "^1.1.1",
|
|
||||||
"magic-string": "^0.30.17",
|
|
||||||
"mlly": "^1.7.4",
|
|
||||||
"pathe": "^2.0.3",
|
|
||||||
"picomatch": "^4.0.2",
|
|
||||||
"pkg-types": "^2.1.0",
|
|
||||||
"scule": "^1.3.0",
|
|
||||||
"strip-literal": "^3.0.0",
|
|
||||||
"tinyglobby": "^0.2.13",
|
|
||||||
"unplugin": "^2.3.2",
|
|
||||||
"unplugin-utils": "^0.2.4"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18.12.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/unimport/node_modules/escape-string-regexp": {
|
|
||||||
"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==",
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/unimport/node_modules/estree-walker": {
|
|
||||||
"version": "3.0.3",
|
|
||||||
"resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-3.0.3.tgz",
|
|
||||||
"integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@types/estree": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/unimport/node_modules/picomatch": {
|
|
||||||
"version": "4.0.2",
|
|
||||||
"resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.2.tgz",
|
|
||||||
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/jonschlinkert"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/unist-util-is": {
|
"node_modules/unist-util-is": {
|
||||||
"version": "6.0.1",
|
"version": "6.0.1",
|
||||||
"resolved": "https://registry.npmmirror.com/unist-util-is/-/unist-util-is-6.0.1.tgz",
|
"resolved": "https://registry.npmmirror.com/unist-util-is/-/unist-util-is-6.0.1.tgz",
|
||||||
@@ -8652,7 +8360,7 @@
|
|||||||
"version": "2.3.10",
|
"version": "2.3.10",
|
||||||
"resolved": "https://registry.npmmirror.com/unplugin/-/unplugin-2.3.10.tgz",
|
"resolved": "https://registry.npmmirror.com/unplugin/-/unplugin-2.3.10.tgz",
|
||||||
"integrity": "sha512-6NCPkv1ClwH+/BGE9QeoTIl09nuiAt0gS28nn1PvYXsGKRwM2TCbFA2QiilmehPDTXIe684k4rZI1yl3A1PCUw==",
|
"integrity": "sha512-6NCPkv1ClwH+/BGE9QeoTIl09nuiAt0gS28nn1PvYXsGKRwM2TCbFA2QiilmehPDTXIe684k4rZI1yl3A1PCUw==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/remapping": "^2.3.5",
|
"@jridgewell/remapping": "^2.3.5",
|
||||||
@@ -8664,36 +8372,6 @@
|
|||||||
"node": ">=18.12.0"
|
"node": ">=18.12.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/unplugin-utils": {
|
|
||||||
"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==",
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
|
||||||
"pathe": "^2.0.2",
|
|
||||||
"picomatch": "^4.0.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18.12.0"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/sxzz"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/unplugin-utils/node_modules/picomatch": {
|
|
||||||
"version": "4.0.2",
|
|
||||||
"resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.2.tgz",
|
|
||||||
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/jonschlinkert"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/unplugin-vue-components": {
|
"node_modules/unplugin-vue-components": {
|
||||||
"version": "30.0.0",
|
"version": "30.0.0",
|
||||||
"resolved": "https://registry.npmmirror.com/unplugin-vue-components/-/unplugin-vue-components-30.0.0.tgz",
|
"resolved": "https://registry.npmmirror.com/unplugin-vue-components/-/unplugin-vue-components-30.0.0.tgz",
|
||||||
@@ -8764,7 +8442,7 @@
|
|||||||
"version": "4.0.3",
|
"version": "4.0.3",
|
||||||
"resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz",
|
"resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz",
|
||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
@@ -8773,23 +8451,6 @@
|
|||||||
"url": "https://github.com/sponsors/jonschlinkert"
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/untyped": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmmirror.com/untyped/-/untyped-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-nwNCjxJTjNuLCgFr42fEak5OcLuB3ecca+9ksPFNvtfYSLpjf+iJqSIaSnIile6ZPbKYxI5k2AfXqeopGudK/g==",
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
|
||||||
"citty": "^0.1.6",
|
|
||||||
"defu": "^6.1.4",
|
|
||||||
"jiti": "^2.4.2",
|
|
||||||
"knitwork": "^1.2.0",
|
|
||||||
"scule": "^1.3.0"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"untyped": "dist/cli.mjs"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/uri-js": {
|
"node_modules/uri-js": {
|
||||||
"version": "4.4.1",
|
"version": "4.4.1",
|
||||||
"resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz",
|
"resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz",
|
||||||
@@ -8891,7 +8552,6 @@
|
|||||||
"integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==",
|
"integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.25.0",
|
"esbuild": "^0.25.0",
|
||||||
"fdir": "^6.5.0",
|
"fdir": "^6.5.0",
|
||||||
@@ -9002,7 +8662,6 @@
|
|||||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@@ -9252,7 +8911,6 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.25.tgz",
|
"resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.25.tgz",
|
||||||
"integrity": "sha512-YLVdgv2K13WJ6n+kD5owehKtEXwdwXuj2TTyJMsO7pSeKw2bfRNZGjhB7YzrpbMYj5b5QsUebHpOqR3R3ziy/g==",
|
"integrity": "sha512-YLVdgv2K13WJ6n+kD5owehKtEXwdwXuj2TTyJMsO7pSeKw2bfRNZGjhB7YzrpbMYj5b5QsUebHpOqR3R3ziy/g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/compiler-dom": "3.5.25",
|
"@vue/compiler-dom": "3.5.25",
|
||||||
"@vue/compiler-sfc": "3.5.25",
|
"@vue/compiler-sfc": "3.5.25",
|
||||||
@@ -9275,7 +8933,6 @@
|
|||||||
"integrity": "sha512-CydUvFOQKD928UzZhTp4pr2vWz1L+H99t7Pkln2QSPdvmURT0MoC4wUccfCnuEaihNsu9aYYyk+bep8rlfkUXw==",
|
"integrity": "sha512-CydUvFOQKD928UzZhTp4pr2vWz1L+H99t7Pkln2QSPdvmURT0MoC4wUccfCnuEaihNsu9aYYyk+bep8rlfkUXw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"debug": "^4.4.0",
|
"debug": "^4.4.0",
|
||||||
"eslint-scope": "^8.2.0",
|
"eslint-scope": "^8.2.0",
|
||||||
@@ -9381,7 +9038,7 @@
|
|||||||
"version": "0.6.2",
|
"version": "0.6.2",
|
||||||
"resolved": "https://registry.npmmirror.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz",
|
"resolved": "https://registry.npmmirror.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz",
|
||||||
"integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==",
|
"integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==",
|
||||||
"devOptional": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/whatwg-mimetype": {
|
"node_modules/whatwg-mimetype": {
|
||||||
|
|||||||
@@ -248,7 +248,7 @@ export const useConfigStore = defineStore('config', () => {
|
|||||||
setFontWeight: (value: string) => updateConfig('fontWeight', value),
|
setFontWeight: (value: string) => updateConfig('fontWeight', value),
|
||||||
|
|
||||||
// 路径操作
|
// 路径操作
|
||||||
setDataPath: (value: string) => updateConfig('dataPath', value),
|
setDataPath: (value: string) => updateConfigLocal('dataPath', value),
|
||||||
|
|
||||||
// 保存配置相关方法
|
// 保存配置相关方法
|
||||||
setAutoSaveDelay: (value: number) => updateConfig('autoSaveDelay', value),
|
setAutoSaveDelay: (value: number) => updateConfig('autoSaveDelay', value),
|
||||||
|
|||||||
@@ -2,16 +2,28 @@
|
|||||||
import {useConfigStore} from '@/stores/configStore';
|
import {useConfigStore} from '@/stores/configStore';
|
||||||
import {useTabStore} from '@/stores/tabStore';
|
import {useTabStore} from '@/stores/tabStore';
|
||||||
import {useI18n} from 'vue-i18n';
|
import {useI18n} from 'vue-i18n';
|
||||||
import {computed, ref} from 'vue';
|
import {computed, ref, onMounted} from 'vue';
|
||||||
import SettingSection from '../components/SettingSection.vue';
|
import SettingSection from '../components/SettingSection.vue';
|
||||||
import SettingItem from '../components/SettingItem.vue';
|
import SettingItem from '../components/SettingItem.vue';
|
||||||
import ToggleSwitch from '../components/ToggleSwitch.vue';
|
import ToggleSwitch from '../components/ToggleSwitch.vue';
|
||||||
import {DialogService, MigrationService} from '@/../bindings/voidraft/internal/services';
|
import {DialogService, HotkeyService, MigrationService} from '@/../bindings/voidraft/internal/services';
|
||||||
import {useSystemStore} from "@/stores/systemStore";
|
import {useSystemStore} from "@/stores/systemStore";
|
||||||
import {useConfirm, usePolling} from '@/composables';
|
import {useConfirm, usePolling} from '@/composables';
|
||||||
|
|
||||||
const {t} = useI18n();
|
const {t} = useI18n();
|
||||||
const configStore = useConfigStore();
|
const {
|
||||||
|
config: {general},
|
||||||
|
resetConfig,
|
||||||
|
setAlwaysOnTop,
|
||||||
|
setDataPath,
|
||||||
|
setEnableGlobalHotkey,
|
||||||
|
setEnableLoadingAnimation,
|
||||||
|
setEnableSystemTray,
|
||||||
|
setEnableTabs,
|
||||||
|
setEnableWindowSnap,
|
||||||
|
setGlobalHotkey,
|
||||||
|
setStartAtLogin
|
||||||
|
} = useConfigStore();
|
||||||
const systemStore = useSystemStore();
|
const systemStore = useSystemStore();
|
||||||
const tabStore = useTabStore();
|
const tabStore = useTabStore();
|
||||||
|
|
||||||
@@ -60,57 +72,57 @@ const hideAll = () => {
|
|||||||
const {isConfirming: isResetConfirming, requestConfirm: requestResetConfirm} = useConfirm({
|
const {isConfirming: isResetConfirming, requestConfirm: requestResetConfirm} = useConfirm({
|
||||||
timeout: 3000,
|
timeout: 3000,
|
||||||
onConfirm: async () => {
|
onConfirm: async () => {
|
||||||
await configStore.resetConfig();
|
await resetConfig();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 可选键列表
|
// 可选键列表 - 从后端获取系统支持的快捷键
|
||||||
const keyOptions = [
|
const keyOptions = ref<string[]>([]);
|
||||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
|
|
||||||
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
|
// 初始化时从后端获取支持的键列表
|
||||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
onMounted(async () => {
|
||||||
'F1', 'F2', 'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'F10', 'F11', 'F12'
|
keyOptions.value = await HotkeyService.GetSupportedKeys();
|
||||||
];
|
});
|
||||||
|
|
||||||
// 计算属性 - 启用全局热键
|
// 计算属性 - 启用全局热键
|
||||||
const enableGlobalHotkey = computed({
|
const enableGlobalHotkey = computed({
|
||||||
get: () => configStore.config.general.enableGlobalHotkey,
|
get: () => general.enableGlobalHotkey,
|
||||||
set: (value: boolean) => configStore.setEnableGlobalHotkey(value)
|
set: (value: boolean) => setEnableGlobalHotkey(value)
|
||||||
});
|
});
|
||||||
|
|
||||||
// 计算属性 - 窗口始终置顶
|
// 计算属性 - 窗口始终置顶
|
||||||
const alwaysOnTop = computed({
|
const alwaysOnTop = computed({
|
||||||
get: () => configStore.config.general.alwaysOnTop,
|
get: () => general.alwaysOnTop,
|
||||||
set: async (value: boolean) => {
|
set: async (value: boolean) => {
|
||||||
// 先更新配置
|
// 先更新配置
|
||||||
await configStore.setAlwaysOnTop(value);
|
await setAlwaysOnTop(value);
|
||||||
await systemStore.setWindowOnTop(value);
|
await systemStore.setWindowOnTop(value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 计算属性 - 启用系统托盘
|
// 计算属性 - 启用系统托盘
|
||||||
const enableSystemTray = computed({
|
const enableSystemTray = computed({
|
||||||
get: () => configStore.config.general.enableSystemTray,
|
get: () => general.enableSystemTray,
|
||||||
set: (value: boolean) => configStore.setEnableSystemTray(value)
|
set: (value: boolean) => setEnableSystemTray(value)
|
||||||
});
|
});
|
||||||
|
|
||||||
// 计算属性 - 启用窗口吸附
|
// 计算属性 - 启用窗口吸附
|
||||||
const enableWindowSnap = computed({
|
const enableWindowSnap = computed({
|
||||||
get: () => configStore.config.general.enableWindowSnap,
|
get: () => general.enableWindowSnap,
|
||||||
set: (value: boolean) => configStore.setEnableWindowSnap(value)
|
set: (value: boolean) => setEnableWindowSnap(value)
|
||||||
});
|
});
|
||||||
|
|
||||||
// 计算属性 - 启用加载动画
|
// 计算属性 - 启用加载动画
|
||||||
const enableLoadingAnimation = computed({
|
const enableLoadingAnimation = computed({
|
||||||
get: () => configStore.config.general.enableLoadingAnimation,
|
get: () => general.enableLoadingAnimation,
|
||||||
set: (value: boolean) => configStore.setEnableLoadingAnimation(value)
|
set: (value: boolean) => setEnableLoadingAnimation(value)
|
||||||
});
|
});
|
||||||
|
|
||||||
// 计算属性 - 启用标签页
|
// 计算属性 - 启用标签页
|
||||||
const enableTabs = computed({
|
const enableTabs = computed({
|
||||||
get: () => configStore.config.general.enableTabs,
|
get: () => general.enableTabs,
|
||||||
set: async (value: boolean) => {
|
set: async (value: boolean) => {
|
||||||
await configStore.setEnableTabs(value);
|
await setEnableTabs(value);
|
||||||
if (value) {
|
if (value) {
|
||||||
// 开启tabs功能时,初始化当前文档到标签页
|
// 开启tabs功能时,初始化当前文档到标签页
|
||||||
tabStore.initializeTab();
|
tabStore.initializeTab();
|
||||||
@@ -123,33 +135,33 @@ const enableTabs = computed({
|
|||||||
|
|
||||||
// 计算属性 - 开机启动
|
// 计算属性 - 开机启动
|
||||||
const startAtLogin = computed({
|
const startAtLogin = computed({
|
||||||
get: () => configStore.config.general.startAtLogin,
|
get: () => general.startAtLogin,
|
||||||
set: (value: boolean) => configStore.setStartAtLogin(value)
|
set: (value: boolean) => setStartAtLogin(value)
|
||||||
});
|
});
|
||||||
|
|
||||||
// 修饰键配置 - 只读计算属性
|
// 修饰键配置 - 只读计算属性
|
||||||
const modifierKeys = computed(() => ({
|
const modifierKeys = computed(() => ({
|
||||||
ctrl: configStore.config.general.globalHotkey.ctrl,
|
ctrl: general.globalHotkey.ctrl,
|
||||||
shift: configStore.config.general.globalHotkey.shift,
|
shift: general.globalHotkey.shift,
|
||||||
alt: configStore.config.general.globalHotkey.alt,
|
alt: general.globalHotkey.alt,
|
||||||
win: configStore.config.general.globalHotkey.win
|
win: general.globalHotkey.win
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// 主键配置
|
// 主键配置
|
||||||
const selectedKey = computed(() => configStore.config.general.globalHotkey.key);
|
const selectedKey = computed(() => general.globalHotkey.key);
|
||||||
|
|
||||||
// 切换修饰键
|
// 切换修饰键
|
||||||
const toggleModifier = (key: 'ctrl' | 'shift' | 'alt' | 'win') => {
|
const toggleModifier = (key: 'ctrl' | 'shift' | 'alt' | 'win') => {
|
||||||
const currentHotkey = configStore.config.general.globalHotkey;
|
const currentHotkey = general.globalHotkey;
|
||||||
const newHotkey = {...currentHotkey, [key]: !currentHotkey[key]};
|
const newHotkey = {...currentHotkey, [key]: !currentHotkey[key]};
|
||||||
configStore.setGlobalHotkey(newHotkey);
|
setGlobalHotkey(newHotkey);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 更新选择的键
|
// 更新选择的键
|
||||||
const updateSelectedKey = (event: Event) => {
|
const updateSelectedKey = (event: Event) => {
|
||||||
const select = event.target as HTMLSelectElement;
|
const select = event.target as HTMLSelectElement;
|
||||||
const newHotkey = {...configStore.config.general.globalHotkey, key: select.value};
|
const newHotkey = {...general.globalHotkey, key: select.value};
|
||||||
configStore.setGlobalHotkey(newHotkey);
|
setGlobalHotkey(newHotkey);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -157,7 +169,7 @@ const updateSelectedKey = (event: Event) => {
|
|||||||
const hotkeyPreview = computed(() => {
|
const hotkeyPreview = computed(() => {
|
||||||
if (!enableGlobalHotkey.value) return '';
|
if (!enableGlobalHotkey.value) return '';
|
||||||
|
|
||||||
const {ctrl, shift, alt, win, key} = configStore.config.general.globalHotkey;
|
const {ctrl, shift, alt, win, key} = general.globalHotkey;
|
||||||
const modifiers = [
|
const modifiers = [
|
||||||
ctrl && 'Ctrl',
|
ctrl && 'Ctrl',
|
||||||
shift && 'Shift',
|
shift && 'Shift',
|
||||||
@@ -170,7 +182,7 @@ const hotkeyPreview = computed(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 数据路径配置
|
// 数据路径配置
|
||||||
const currentDataPath = computed(() => configStore.config.general.dataPath);
|
const currentDataPath = computed(() => general.dataPath);
|
||||||
|
|
||||||
// 选择数据存储目录
|
// 选择数据存储目录
|
||||||
const selectDataDirectory = async () => {
|
const selectDataDirectory = async () => {
|
||||||
@@ -189,7 +201,7 @@ const selectDataDirectory = async () => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await MigrationService.MigrateDirectory(oldPath, newPath);
|
await MigrationService.MigrateDirectory(oldPath, newPath);
|
||||||
await configStore.setDataPath(newPath);
|
await setDataPath(newPath);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
stop();
|
stop();
|
||||||
// 设置手动捕获的错误(当轮询还没获取到错误时)
|
// 设置手动捕获的错误(当轮询还没获取到错误时)
|
||||||
@@ -314,10 +326,6 @@ const selectDataDirectory = async () => {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.settings-page {
|
|
||||||
//max-width: 800px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hotkey-selector {
|
.hotkey-selector {
|
||||||
padding: 15px 0 5px 20px;
|
padding: 15px 0 5px 20px;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package services
|
package helper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -10,6 +10,8 @@ import (
|
|||||||
"github.com/wailsapp/wails/v3/pkg/services/log"
|
"github.com/wailsapp/wails/v3/pkg/services/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const pathSeparator = '.'
|
||||||
|
|
||||||
// ObserverCallback 观察者回调函数
|
// ObserverCallback 观察者回调函数
|
||||||
type ObserverCallback func(oldValue, newValue interface{})
|
type ObserverCallback func(oldValue, newValue interface{})
|
||||||
|
|
||||||
@@ -49,6 +51,8 @@ func NewConfigObserver(logger *log.LogService) *ConfigObserver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Watch 注册配置变更监听器
|
// Watch 注册配置变更监听器
|
||||||
|
// 支持前缀监听:注册 "generate" 可以监听 "generate.xxx"、"generate.yyy" 等所有子路径的变化
|
||||||
|
// 返回取消函数,调用后停止监听
|
||||||
func (co *ConfigObserver) Watch(path string, callback ObserverCallback) CancelFunc {
|
func (co *ConfigObserver) Watch(path string, callback ObserverCallback) CancelFunc {
|
||||||
// 生成唯一ID
|
// 生成唯一ID
|
||||||
id := fmt.Sprintf("obs_%d", co.nextObserverID.Add(1))
|
id := fmt.Sprintf("obs_%d", co.nextObserverID.Add(1))
|
||||||
@@ -103,36 +107,91 @@ func (co *ConfigObserver) removeObserver(path, id string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify 通知指定路径的所有观察者
|
// Notify 通知指定路径及其所有父路径的观察者
|
||||||
|
// 支持前缀监听:当 "generate.xxx" 变化时,同时通知监听 "generate" 的观察者
|
||||||
|
// 通知顺序:精确匹配 -> 父路径(从近到远)
|
||||||
func (co *ConfigObserver) Notify(path string, oldValue, newValue interface{}) {
|
func (co *ConfigObserver) Notify(path string, oldValue, newValue interface{}) {
|
||||||
// 获取该路径的所有观察者(拷贝以避免并发问题)
|
|
||||||
co.observerMu.RLock()
|
co.observerMu.RLock()
|
||||||
observers := co.observers[path]
|
callbacks := co.collectCallbacks(path)
|
||||||
if len(observers) == 0 {
|
|
||||||
co.observerMu.RUnlock()
|
co.observerMu.RUnlock()
|
||||||
|
|
||||||
|
if len(callbacks) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 拷贝观察者列表
|
// 执行所有回调
|
||||||
callbacks := make([]ObserverCallback, len(observers))
|
|
||||||
for i, obs := range observers {
|
|
||||||
callbacks[i] = obs.callback
|
|
||||||
}
|
|
||||||
co.observerMu.RUnlock()
|
|
||||||
|
|
||||||
// 在独立 goroutine 中执行回调
|
|
||||||
for _, callback := range callbacks {
|
for _, callback := range callbacks {
|
||||||
co.executeCallback(callback, oldValue, newValue)
|
co.executeCallback(callback, oldValue, newValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NotifyAll 通知所有匹配前缀的观察者
|
// collectCallbacks 收集指定路径及其所有父路径的观察者回调
|
||||||
|
// 调用者必须持有读锁
|
||||||
|
func (co *ConfigObserver) collectCallbacks(path string) []ObserverCallback {
|
||||||
|
if path == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var callbacks []ObserverCallback
|
||||||
|
|
||||||
|
// 1. 收集精确匹配的观察者
|
||||||
|
if observers := co.observers[path]; len(observers) > 0 {
|
||||||
|
callbacks = make([]ObserverCallback, 0, len(observers)*2)
|
||||||
|
for _, obs := range observers {
|
||||||
|
callbacks = append(callbacks, obs.callback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 收集父路径的观察者(从后向前遍历,避免 strings.Split 的内存分配)
|
||||||
|
for i := len(path) - 1; i >= 0; i-- {
|
||||||
|
if path[i] == pathSeparator {
|
||||||
|
parentPath := path[:i]
|
||||||
|
if observers := co.observers[parentPath]; len(observers) > 0 {
|
||||||
|
if callbacks == nil {
|
||||||
|
callbacks = make([]ObserverCallback, 0, len(observers))
|
||||||
|
}
|
||||||
|
for _, obs := range observers {
|
||||||
|
callbacks = append(callbacks, obs.callback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return callbacks
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotifyAll 批量通知所有匹配路径的观察者
|
||||||
func (co *ConfigObserver) NotifyAll(changes map[string]struct {
|
func (co *ConfigObserver) NotifyAll(changes map[string]struct {
|
||||||
OldValue interface{}
|
OldValue interface{}
|
||||||
NewValue interface{}
|
NewValue interface{}
|
||||||
}) {
|
}) {
|
||||||
|
if len(changes) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type callbackTask struct {
|
||||||
|
callback ObserverCallback
|
||||||
|
oldValue interface{}
|
||||||
|
newValue interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只获取一次读锁,收集所有回调
|
||||||
|
co.observerMu.RLock()
|
||||||
|
var tasks []callbackTask
|
||||||
for path, change := range changes {
|
for path, change := range changes {
|
||||||
co.Notify(path, change.OldValue, change.NewValue)
|
for _, cb := range co.collectCallbacks(path) {
|
||||||
|
tasks = append(tasks, callbackTask{
|
||||||
|
callback: cb,
|
||||||
|
oldValue: change.OldValue,
|
||||||
|
newValue: change.NewValue,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
co.observerMu.RUnlock()
|
||||||
|
|
||||||
|
// 执行所有回调
|
||||||
|
for _, task := range tasks {
|
||||||
|
co.executeCallback(task.callback, task.oldValue, task.newValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ type Document struct {
|
|||||||
// ID of the ent.
|
// ID of the ent.
|
||||||
ID int `json:"id,omitempty"`
|
ID int `json:"id,omitempty"`
|
||||||
// UUID for cross-device sync (UUIDv7)
|
// UUID for cross-device sync (UUIDv7)
|
||||||
UUID string `json:"uuid"`
|
UUID *string `json:"uuid"`
|
||||||
// creation time
|
// creation time
|
||||||
CreatedAt string `json:"created_at"`
|
CreatedAt string `json:"created_at"`
|
||||||
// update time
|
// update time
|
||||||
@@ -69,7 +69,8 @@ func (_m *Document) assignValues(columns []string, values []any) error {
|
|||||||
if value, ok := values[i].(*sql.NullString); !ok {
|
if value, ok := values[i].(*sql.NullString); !ok {
|
||||||
return fmt.Errorf("unexpected type %T for field uuid", values[i])
|
return fmt.Errorf("unexpected type %T for field uuid", values[i])
|
||||||
} else if value.Valid {
|
} else if value.Valid {
|
||||||
_m.UUID = value.String
|
_m.UUID = new(string)
|
||||||
|
*_m.UUID = value.String
|
||||||
}
|
}
|
||||||
case document.FieldCreatedAt:
|
case document.FieldCreatedAt:
|
||||||
if value, ok := values[i].(*sql.NullString); !ok {
|
if value, ok := values[i].(*sql.NullString); !ok {
|
||||||
@@ -144,8 +145,10 @@ func (_m *Document) String() string {
|
|||||||
var builder strings.Builder
|
var builder strings.Builder
|
||||||
builder.WriteString("Document(")
|
builder.WriteString("Document(")
|
||||||
builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID))
|
builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID))
|
||||||
|
if v := _m.UUID; v != nil {
|
||||||
builder.WriteString("uuid=")
|
builder.WriteString("uuid=")
|
||||||
builder.WriteString(_m.UUID)
|
builder.WriteString(*v)
|
||||||
|
}
|
||||||
builder.WriteString(", ")
|
builder.WriteString(", ")
|
||||||
builder.WriteString("created_at=")
|
builder.WriteString("created_at=")
|
||||||
builder.WriteString(_m.CreatedAt)
|
builder.WriteString(_m.CreatedAt)
|
||||||
|
|||||||
@@ -225,7 +225,7 @@ func (_c *DocumentCreate) createSpec() (*Document, *sqlgraph.CreateSpec) {
|
|||||||
)
|
)
|
||||||
if value, ok := _c.mutation.UUID(); ok {
|
if value, ok := _c.mutation.UUID(); ok {
|
||||||
_spec.SetField(document.FieldUUID, field.TypeString, value)
|
_spec.SetField(document.FieldUUID, field.TypeString, value)
|
||||||
_node.UUID = value
|
_node.UUID = &value
|
||||||
}
|
}
|
||||||
if value, ok := _c.mutation.CreatedAt(); ok {
|
if value, ok := _c.mutation.CreatedAt(); ok {
|
||||||
_spec.SetField(document.FieldCreatedAt, field.TypeString, value)
|
_spec.SetField(document.FieldCreatedAt, field.TypeString, value)
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ type Extension struct {
|
|||||||
// ID of the ent.
|
// ID of the ent.
|
||||||
ID int `json:"id,omitempty"`
|
ID int `json:"id,omitempty"`
|
||||||
// UUID for cross-device sync (UUIDv7)
|
// UUID for cross-device sync (UUIDv7)
|
||||||
UUID string `json:"uuid"`
|
UUID *string `json:"uuid"`
|
||||||
// creation time
|
// creation time
|
||||||
CreatedAt string `json:"created_at"`
|
CreatedAt string `json:"created_at"`
|
||||||
// update time
|
// update time
|
||||||
@@ -72,7 +72,8 @@ func (_m *Extension) assignValues(columns []string, values []any) error {
|
|||||||
if value, ok := values[i].(*sql.NullString); !ok {
|
if value, ok := values[i].(*sql.NullString); !ok {
|
||||||
return fmt.Errorf("unexpected type %T for field uuid", values[i])
|
return fmt.Errorf("unexpected type %T for field uuid", values[i])
|
||||||
} else if value.Valid {
|
} else if value.Valid {
|
||||||
_m.UUID = value.String
|
_m.UUID = new(string)
|
||||||
|
*_m.UUID = value.String
|
||||||
}
|
}
|
||||||
case extension.FieldCreatedAt:
|
case extension.FieldCreatedAt:
|
||||||
if value, ok := values[i].(*sql.NullString); !ok {
|
if value, ok := values[i].(*sql.NullString); !ok {
|
||||||
@@ -149,8 +150,10 @@ func (_m *Extension) String() string {
|
|||||||
var builder strings.Builder
|
var builder strings.Builder
|
||||||
builder.WriteString("Extension(")
|
builder.WriteString("Extension(")
|
||||||
builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID))
|
builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID))
|
||||||
|
if v := _m.UUID; v != nil {
|
||||||
builder.WriteString("uuid=")
|
builder.WriteString("uuid=")
|
||||||
builder.WriteString(_m.UUID)
|
builder.WriteString(*v)
|
||||||
|
}
|
||||||
builder.WriteString(", ")
|
builder.WriteString(", ")
|
||||||
builder.WriteString("created_at=")
|
builder.WriteString("created_at=")
|
||||||
builder.WriteString(_m.CreatedAt)
|
builder.WriteString(_m.CreatedAt)
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ func (_c *ExtensionCreate) createSpec() (*Extension, *sqlgraph.CreateSpec) {
|
|||||||
)
|
)
|
||||||
if value, ok := _c.mutation.UUID(); ok {
|
if value, ok := _c.mutation.UUID(); ok {
|
||||||
_spec.SetField(extension.FieldUUID, field.TypeString, value)
|
_spec.SetField(extension.FieldUUID, field.TypeString, value)
|
||||||
_node.UUID = value
|
_node.UUID = &value
|
||||||
}
|
}
|
||||||
if value, ok := _c.mutation.CreatedAt(); ok {
|
if value, ok := _c.mutation.CreatedAt(); ok {
|
||||||
_spec.SetField(extension.FieldCreatedAt, field.TypeString, value)
|
_spec.SetField(extension.FieldCreatedAt, field.TypeString, value)
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ type KeyBinding struct {
|
|||||||
// ID of the ent.
|
// ID of the ent.
|
||||||
ID int `json:"id,omitempty"`
|
ID int `json:"id,omitempty"`
|
||||||
// UUID for cross-device sync (UUIDv7)
|
// UUID for cross-device sync (UUIDv7)
|
||||||
UUID string `json:"uuid"`
|
UUID *string `json:"uuid"`
|
||||||
// creation time
|
// creation time
|
||||||
CreatedAt string `json:"created_at"`
|
CreatedAt string `json:"created_at"`
|
||||||
// update time
|
// update time
|
||||||
@@ -71,7 +71,8 @@ func (_m *KeyBinding) assignValues(columns []string, values []any) error {
|
|||||||
if value, ok := values[i].(*sql.NullString); !ok {
|
if value, ok := values[i].(*sql.NullString); !ok {
|
||||||
return fmt.Errorf("unexpected type %T for field uuid", values[i])
|
return fmt.Errorf("unexpected type %T for field uuid", values[i])
|
||||||
} else if value.Valid {
|
} else if value.Valid {
|
||||||
_m.UUID = value.String
|
_m.UUID = new(string)
|
||||||
|
*_m.UUID = value.String
|
||||||
}
|
}
|
||||||
case keybinding.FieldCreatedAt:
|
case keybinding.FieldCreatedAt:
|
||||||
if value, ok := values[i].(*sql.NullString); !ok {
|
if value, ok := values[i].(*sql.NullString); !ok {
|
||||||
@@ -152,8 +153,10 @@ func (_m *KeyBinding) String() string {
|
|||||||
var builder strings.Builder
|
var builder strings.Builder
|
||||||
builder.WriteString("KeyBinding(")
|
builder.WriteString("KeyBinding(")
|
||||||
builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID))
|
builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID))
|
||||||
|
if v := _m.UUID; v != nil {
|
||||||
builder.WriteString("uuid=")
|
builder.WriteString("uuid=")
|
||||||
builder.WriteString(_m.UUID)
|
builder.WriteString(*v)
|
||||||
|
}
|
||||||
builder.WriteString(", ")
|
builder.WriteString(", ")
|
||||||
builder.WriteString("created_at=")
|
builder.WriteString("created_at=")
|
||||||
builder.WriteString(_m.CreatedAt)
|
builder.WriteString(_m.CreatedAt)
|
||||||
|
|||||||
@@ -240,7 +240,7 @@ func (_c *KeyBindingCreate) createSpec() (*KeyBinding, *sqlgraph.CreateSpec) {
|
|||||||
)
|
)
|
||||||
if value, ok := _c.mutation.UUID(); ok {
|
if value, ok := _c.mutation.UUID(); ok {
|
||||||
_spec.SetField(keybinding.FieldUUID, field.TypeString, value)
|
_spec.SetField(keybinding.FieldUUID, field.TypeString, value)
|
||||||
_node.UUID = value
|
_node.UUID = &value
|
||||||
}
|
}
|
||||||
if value, ok := _c.mutation.CreatedAt(); ok {
|
if value, ok := _c.mutation.CreatedAt(); ok {
|
||||||
_spec.SetField(keybinding.FieldCreatedAt, field.TypeString, value)
|
_spec.SetField(keybinding.FieldCreatedAt, field.TypeString, value)
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ func (m *DocumentMutation) UUID() (r string, exists bool) {
|
|||||||
// OldUUID returns the old "uuid" field's value of the Document entity.
|
// OldUUID returns the old "uuid" field's value of the Document entity.
|
||||||
// If the Document object wasn't provided to the builder, the object is fetched from the database.
|
// If the Document object wasn't provided to the builder, the object is fetched from the database.
|
||||||
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
|
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
|
||||||
func (m *DocumentMutation) OldUUID(ctx context.Context) (v string, err error) {
|
func (m *DocumentMutation) OldUUID(ctx context.Context) (v *string, err error) {
|
||||||
if !m.op.Is(OpUpdateOne) {
|
if !m.op.Is(OpUpdateOne) {
|
||||||
return v, errors.New("OldUUID is only allowed on UpdateOne operations")
|
return v, errors.New("OldUUID is only allowed on UpdateOne operations")
|
||||||
}
|
}
|
||||||
@@ -876,7 +876,7 @@ func (m *ExtensionMutation) UUID() (r string, exists bool) {
|
|||||||
// OldUUID returns the old "uuid" field's value of the Extension entity.
|
// OldUUID returns the old "uuid" field's value of the Extension entity.
|
||||||
// If the Extension object wasn't provided to the builder, the object is fetched from the database.
|
// If the Extension object wasn't provided to the builder, the object is fetched from the database.
|
||||||
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
|
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
|
||||||
func (m *ExtensionMutation) OldUUID(ctx context.Context) (v string, err error) {
|
func (m *ExtensionMutation) OldUUID(ctx context.Context) (v *string, err error) {
|
||||||
if !m.op.Is(OpUpdateOne) {
|
if !m.op.Is(OpUpdateOne) {
|
||||||
return v, errors.New("OldUUID is only allowed on UpdateOne operations")
|
return v, errors.New("OldUUID is only allowed on UpdateOne operations")
|
||||||
}
|
}
|
||||||
@@ -1587,7 +1587,7 @@ func (m *KeyBindingMutation) UUID() (r string, exists bool) {
|
|||||||
// OldUUID returns the old "uuid" field's value of the KeyBinding entity.
|
// OldUUID returns the old "uuid" field's value of the KeyBinding entity.
|
||||||
// If the KeyBinding object wasn't provided to the builder, the object is fetched from the database.
|
// If the KeyBinding object wasn't provided to the builder, the object is fetched from the database.
|
||||||
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
|
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
|
||||||
func (m *KeyBindingMutation) OldUUID(ctx context.Context) (v string, err error) {
|
func (m *KeyBindingMutation) OldUUID(ctx context.Context) (v *string, err error) {
|
||||||
if !m.op.Is(OpUpdateOne) {
|
if !m.op.Is(OpUpdateOne) {
|
||||||
return v, errors.New("OldUUID is only allowed on UpdateOne operations")
|
return v, errors.New("OldUUID is only allowed on UpdateOne operations")
|
||||||
}
|
}
|
||||||
@@ -2350,7 +2350,7 @@ func (m *ThemeMutation) UUID() (r string, exists bool) {
|
|||||||
// OldUUID returns the old "uuid" field's value of the Theme entity.
|
// OldUUID returns the old "uuid" field's value of the Theme entity.
|
||||||
// If the Theme object wasn't provided to the builder, the object is fetched from the database.
|
// If the Theme object wasn't provided to the builder, the object is fetched from the database.
|
||||||
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
|
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
|
||||||
func (m *ThemeMutation) OldUUID(ctx context.Context) (v string, err error) {
|
func (m *ThemeMutation) OldUUID(ctx context.Context) (v *string, err error) {
|
||||||
if !m.op.Is(OpUpdateOne) {
|
if !m.op.Is(OpUpdateOne) {
|
||||||
return v, errors.New("OldUUID is only allowed on UpdateOne operations")
|
return v, errors.New("OldUUID is only allowed on UpdateOne operations")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ type Theme struct {
|
|||||||
// ID of the ent.
|
// ID of the ent.
|
||||||
ID int `json:"id,omitempty"`
|
ID int `json:"id,omitempty"`
|
||||||
// UUID for cross-device sync (UUIDv7)
|
// UUID for cross-device sync (UUIDv7)
|
||||||
UUID string `json:"uuid"`
|
UUID *string `json:"uuid"`
|
||||||
// creation time
|
// creation time
|
||||||
CreatedAt string `json:"created_at"`
|
CreatedAt string `json:"created_at"`
|
||||||
// update time
|
// update time
|
||||||
@@ -70,7 +70,8 @@ func (_m *Theme) assignValues(columns []string, values []any) error {
|
|||||||
if value, ok := values[i].(*sql.NullString); !ok {
|
if value, ok := values[i].(*sql.NullString); !ok {
|
||||||
return fmt.Errorf("unexpected type %T for field uuid", values[i])
|
return fmt.Errorf("unexpected type %T for field uuid", values[i])
|
||||||
} else if value.Valid {
|
} else if value.Valid {
|
||||||
_m.UUID = value.String
|
_m.UUID = new(string)
|
||||||
|
*_m.UUID = value.String
|
||||||
}
|
}
|
||||||
case theme.FieldCreatedAt:
|
case theme.FieldCreatedAt:
|
||||||
if value, ok := values[i].(*sql.NullString); !ok {
|
if value, ok := values[i].(*sql.NullString); !ok {
|
||||||
@@ -147,8 +148,10 @@ func (_m *Theme) String() string {
|
|||||||
var builder strings.Builder
|
var builder strings.Builder
|
||||||
builder.WriteString("Theme(")
|
builder.WriteString("Theme(")
|
||||||
builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID))
|
builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID))
|
||||||
|
if v := _m.UUID; v != nil {
|
||||||
builder.WriteString("uuid=")
|
builder.WriteString("uuid=")
|
||||||
builder.WriteString(_m.UUID)
|
builder.WriteString(*v)
|
||||||
|
}
|
||||||
builder.WriteString(", ")
|
builder.WriteString(", ")
|
||||||
builder.WriteString("created_at=")
|
builder.WriteString("created_at=")
|
||||||
builder.WriteString(_m.CreatedAt)
|
builder.WriteString(_m.CreatedAt)
|
||||||
|
|||||||
@@ -206,7 +206,7 @@ func (_c *ThemeCreate) createSpec() (*Theme, *sqlgraph.CreateSpec) {
|
|||||||
)
|
)
|
||||||
if value, ok := _c.mutation.UUID(); ok {
|
if value, ok := _c.mutation.UUID(); ok {
|
||||||
_spec.SetField(theme.FieldUUID, field.TypeString, value)
|
_spec.SetField(theme.FieldUUID, field.TypeString, value)
|
||||||
_node.UUID = value
|
_node.UUID = &value
|
||||||
}
|
}
|
||||||
if value, ok := _c.mutation.CreatedAt(); ok {
|
if value, ok := _c.mutation.CreatedAt(); ok {
|
||||||
_spec.SetField(theme.FieldCreatedAt, field.TypeString, value)
|
_spec.SetField(theme.FieldCreatedAt, field.TypeString, value)
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
"voidraft/internal/common/helper"
|
||||||
|
|
||||||
"github.com/go-git/go-git/v5"
|
"github.com/go-git/go-git/v5"
|
||||||
gitConfig "github.com/go-git/go-git/v5/config"
|
gitConfig "github.com/go-git/go-git/v5/config"
|
||||||
@@ -61,7 +62,7 @@ type BackupService struct {
|
|||||||
autoBackupStop chan bool
|
autoBackupStop chan bool
|
||||||
autoBackupWg sync.WaitGroup
|
autoBackupWg sync.WaitGroup
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
cancelObserver CancelFunc
|
cancelObservers []helper.CancelFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBackupService 创建新的备份服务实例
|
// NewBackupService 创建新的备份服务实例
|
||||||
@@ -74,7 +75,11 @@ func NewBackupService(configService *ConfigService, dbService *DatabaseService,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *BackupService) ServiceStartup(ctx context.Context, options application.ServiceOptions) error {
|
func (s *BackupService) ServiceStartup(ctx context.Context, options application.ServiceOptions) error {
|
||||||
s.cancelObserver = s.configService.Watch("backup.enabled", s.onBackupConfigChange)
|
// 监听 backup 配置变化
|
||||||
|
s.cancelObservers = []helper.CancelFunc{
|
||||||
|
s.configService.Watch("backup", s.onBackupConfigChange),
|
||||||
|
s.configService.Watch("general.dataPath", s.onDataPathChange),
|
||||||
|
}
|
||||||
if err := s.Initialize(); err != nil {
|
if err := s.Initialize(); err != nil {
|
||||||
s.logger.Error("initializing backup service: %v", err)
|
s.logger.Error("initializing backup service: %v", err)
|
||||||
}
|
}
|
||||||
@@ -89,6 +94,12 @@ func (s *BackupService) onBackupConfigChange(oldValue, newValue interface{}) {
|
|||||||
_ = s.HandleConfigChange(&config.Backup)
|
_ = s.HandleConfigChange(&config.Backup)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *BackupService) onDataPathChange(oldValue, newValue interface{}) {
|
||||||
|
if err := s.Reinitialize(); err != nil {
|
||||||
|
s.logger.Error("Failed to reinitialize backup service after data path change: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize 初始化备份服务
|
// Initialize 初始化备份服务
|
||||||
func (s *BackupService) Initialize() error {
|
func (s *BackupService) Initialize() error {
|
||||||
config, repoPath, err := s.getConfigAndPath()
|
config, repoPath, err := s.getConfigAndPath()
|
||||||
@@ -100,7 +111,7 @@ func (s *BackupService) Initialize() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 仓库地址为空时不初始化(等待用户配置)
|
// 仓库地址为空时不初始化
|
||||||
if strings.TrimSpace(config.RepoURL) == "" {
|
if strings.TrimSpace(config.RepoURL) == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -1196,8 +1207,10 @@ func (s *BackupService) HandleConfigChange(config *models.GitBackupConfig) error
|
|||||||
|
|
||||||
// ServiceShutdown 服务关闭
|
// ServiceShutdown 服务关闭
|
||||||
func (s *BackupService) ServiceShutdown() {
|
func (s *BackupService) ServiceShutdown() {
|
||||||
if s.cancelObserver != nil {
|
for _, cancel := range s.cancelObservers {
|
||||||
s.cancelObserver()
|
if cancel != nil {
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
s.StopAutoBackup()
|
s.StopAutoBackup()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,33 +2,31 @@ package services
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/wailsapp/wails/v3/pkg/application"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
"voidraft/internal/common/helper"
|
||||||
"voidraft/internal/models"
|
"voidraft/internal/models"
|
||||||
|
|
||||||
jsonparser "github.com/knadh/koanf/parsers/json"
|
jsonparser "github.com/knadh/koanf/parsers/json"
|
||||||
"github.com/knadh/koanf/providers/file"
|
"github.com/knadh/koanf/providers/file"
|
||||||
"github.com/knadh/koanf/providers/structs"
|
"github.com/knadh/koanf/providers/structs"
|
||||||
"github.com/knadh/koanf/v2"
|
"github.com/knadh/koanf/v2"
|
||||||
|
"github.com/wailsapp/wails/v3/pkg/application"
|
||||||
"github.com/wailsapp/wails/v3/pkg/services/log"
|
"github.com/wailsapp/wails/v3/pkg/services/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ConfigService 应用配置服务
|
// ConfigService 应用配置服务
|
||||||
type ConfigService struct {
|
type ConfigService struct {
|
||||||
koanf *koanf.Koanf // koanf 实例
|
koanf *koanf.Koanf
|
||||||
logger *log.LogService // 日志服务
|
logger *log.LogService
|
||||||
configDir string // 配置目录
|
configDir string
|
||||||
settingsPath string // 设置文件路径
|
settingsPath string
|
||||||
mu sync.RWMutex // 读写锁
|
mu sync.RWMutex
|
||||||
fileProvider *file.File // 文件提供器,用于监听
|
observer *helper.ConfigObserver
|
||||||
|
|
||||||
observer *ConfigObserver
|
|
||||||
|
|
||||||
// 配置迁移器
|
// 配置迁移器
|
||||||
configMigrator *ConfigMigrator
|
configMigrator *ConfigMigrator
|
||||||
@@ -36,49 +34,29 @@ type ConfigService struct {
|
|||||||
|
|
||||||
// NewConfigService 创建新的配置服务实例
|
// NewConfigService 创建新的配置服务实例
|
||||||
func NewConfigService(logger *log.LogService) *ConfigService {
|
func NewConfigService(logger *log.LogService) *ConfigService {
|
||||||
// 获取用户主目录
|
|
||||||
homeDir, err := os.UserHomeDir()
|
homeDir, err := os.UserHomeDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("unable to get the user's home directory: %w", err))
|
panic(fmt.Errorf("unable to get the user's home directory: %w", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置配置目录和设置文件路径
|
|
||||||
configDir := filepath.Join(homeDir, ".voidraft", "config")
|
configDir := filepath.Join(homeDir, ".voidraft", "config")
|
||||||
settingsPath := filepath.Join(configDir, "settings.json")
|
settingsPath := filepath.Join(configDir, "settings.json")
|
||||||
|
|
||||||
observerService := NewConfigObserver(logger)
|
|
||||||
|
|
||||||
configMigrator := NewConfigMigrator(logger, configDir, "settings", settingsPath)
|
|
||||||
|
|
||||||
return &ConfigService{
|
return &ConfigService{
|
||||||
logger: logger,
|
logger: logger,
|
||||||
configDir: configDir,
|
configDir: configDir,
|
||||||
settingsPath: settingsPath,
|
settingsPath: settingsPath,
|
||||||
koanf: koanf.New("."),
|
koanf: koanf.New("."),
|
||||||
observer: observerService,
|
observer: helper.NewConfigObserver(logger),
|
||||||
configMigrator: configMigrator,
|
configMigrator: NewConfigMigrator(logger, configDir, "settings", settingsPath),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServiceStartup initializes the service when the application starts
|
// ServiceStartup 服务启动时初始化
|
||||||
func (cs *ConfigService) ServiceStartup(ctx context.Context, options application.ServiceOptions) error {
|
func (cs *ConfigService) ServiceStartup(ctx context.Context, options application.ServiceOptions) error {
|
||||||
err := cs.initConfig()
|
if err := cs.initConfig(); err != nil {
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
// 启动配置文件监听
|
|
||||||
cs.startWatching()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// setDefaults 设置默认配置
|
|
||||||
func (cs *ConfigService) setDefaults() error {
|
|
||||||
defaultConfig := models.NewDefaultAppConfig()
|
|
||||||
|
|
||||||
if err := cs.koanf.Load(structs.Provider(defaultConfig, "json"), nil); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,15 +65,38 @@ func (cs *ConfigService) initConfig() error {
|
|||||||
cs.mu.Lock()
|
cs.mu.Lock()
|
||||||
defer cs.mu.Unlock()
|
defer cs.mu.Unlock()
|
||||||
|
|
||||||
// 检查配置文件是否存在
|
// 确保配置目录存在
|
||||||
|
if err := os.MkdirAll(cs.configDir, 0755); err != nil {
|
||||||
|
return fmt.Errorf("failed to create config directory: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 配置文件不存在,创建默认配置
|
||||||
if _, err := os.Stat(cs.settingsPath); os.IsNotExist(err) {
|
if _, err := os.Stat(cs.settingsPath); os.IsNotExist(err) {
|
||||||
return cs.createDefaultConfig()
|
return cs.createDefaultConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 配置文件存在,直接加载现有配置
|
// 加载现有配置
|
||||||
cs.fileProvider = file.Provider(cs.settingsPath)
|
if err := cs.koanf.Load(file.Provider(cs.settingsPath), jsonparser.Parser()); err != nil {
|
||||||
if err := cs.koanf.Load(cs.fileProvider, jsonparser.Parser()); err != nil {
|
return fmt.Errorf("failed to load config: %w", err)
|
||||||
return err
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// createDefaultConfig 创建默认配置
|
||||||
|
func (cs *ConfigService) createDefaultConfig() error {
|
||||||
|
// 重置 koanf 实例
|
||||||
|
cs.koanf = koanf.New(".")
|
||||||
|
|
||||||
|
// 加载默认配置
|
||||||
|
defaultConfig := models.NewDefaultAppConfig()
|
||||||
|
if err := cs.koanf.Load(structs.Provider(defaultConfig, "json"), nil); err != nil {
|
||||||
|
return fmt.Errorf("failed to load default config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 写入配置文件
|
||||||
|
if err := cs.writeConfigToFile(); err != nil {
|
||||||
|
return fmt.Errorf("failed to write default config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -107,65 +108,12 @@ func (cs *ConfigService) MigrateConfig() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cs.mu.Lock()
|
||||||
|
defer cs.mu.Unlock()
|
||||||
|
|
||||||
defaultConfig := models.NewDefaultAppConfig()
|
defaultConfig := models.NewDefaultAppConfig()
|
||||||
_, err := cs.configMigrator.AutoMigrate(defaultConfig, cs.koanf)
|
_, err := cs.configMigrator.AutoMigrate(defaultConfig, cs.koanf)
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// createDefaultConfig 创建默认配置文件
|
|
||||||
func (cs *ConfigService) createDefaultConfig() error {
|
|
||||||
// 确保配置目录存在
|
|
||||||
if err := os.MkdirAll(cs.configDir, 0755); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := cs.setDefaults(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := cs.writeConfigToFile(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建文件提供器
|
|
||||||
cs.fileProvider = file.Provider(cs.settingsPath)
|
|
||||||
|
|
||||||
if err := cs.koanf.Load(cs.fileProvider, jsonparser.Parser()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// startWatching 启动配置文件监听
|
|
||||||
func (cs *ConfigService) startWatching() {
|
|
||||||
if cs.fileProvider == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
cs.fileProvider.Watch(func(event interface{}, err error) {
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
cs.mu.Lock()
|
|
||||||
oldSnapshot := cs.createConfigSnapshot()
|
|
||||||
cs.koanf.Load(cs.fileProvider, jsonparser.Parser())
|
|
||||||
newSnapshot := cs.createConfigSnapshot()
|
|
||||||
cs.mu.Unlock()
|
|
||||||
|
|
||||||
// 检测配置变更并通知观察者
|
|
||||||
cs.notifyChanges(oldSnapshot, newSnapshot)
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// stopWatching 停止配置文件监听
|
|
||||||
func (cs *ConfigService) stopWatching() {
|
|
||||||
if cs.fileProvider != nil {
|
|
||||||
cs.fileProvider.Unwatch()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetConfig 获取完整应用配置
|
// GetConfig 获取完整应用配置
|
||||||
@@ -177,47 +125,9 @@ func (cs *ConfigService) GetConfig() (*models.AppConfig, error) {
|
|||||||
if err := cs.koanf.UnmarshalWithConf("", &config, koanf.UnmarshalConf{Tag: "json"}); err != nil {
|
if err := cs.koanf.UnmarshalWithConf("", &config, koanf.UnmarshalConf{Tag: "json"}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &config, nil
|
return &config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set 设置配置项
|
|
||||||
func (cs *ConfigService) Set(key string, value interface{}) error {
|
|
||||||
cs.mu.Lock()
|
|
||||||
|
|
||||||
// 获取旧值用于回滚
|
|
||||||
oldValue := cs.koanf.Get(key)
|
|
||||||
|
|
||||||
// 设置值到koanf
|
|
||||||
cs.koanf.Set(key, value)
|
|
||||||
|
|
||||||
// 更新时间戳
|
|
||||||
newTimestamp := time.Now().Format(time.RFC3339)
|
|
||||||
cs.koanf.Set("metadata.lastUpdated", newTimestamp)
|
|
||||||
|
|
||||||
// 将配置写回文件
|
|
||||||
err := cs.writeConfigToFile()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
// 写文件失败,回滚内存状态
|
|
||||||
if oldValue != nil {
|
|
||||||
cs.koanf.Set(key, oldValue)
|
|
||||||
} else {
|
|
||||||
cs.koanf.Delete(key)
|
|
||||||
}
|
|
||||||
cs.mu.Unlock()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
cs.mu.Unlock()
|
|
||||||
|
|
||||||
if cs.observer != nil {
|
|
||||||
cs.observer.Notify(key, oldValue, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get 获取配置项
|
// Get 获取配置项
|
||||||
func (cs *ConfigService) Get(key string) interface{} {
|
func (cs *ConfigService) Get(key string) interface{} {
|
||||||
cs.mu.RLock()
|
cs.mu.RLock()
|
||||||
@@ -225,118 +135,113 @@ func (cs *ConfigService) Get(key string) interface{} {
|
|||||||
return cs.koanf.Get(key)
|
return cs.koanf.Get(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResetConfig 强制重置所有配置为默认值
|
// Set 设置配置项
|
||||||
|
func (cs *ConfigService) Set(key string, value interface{}) error {
|
||||||
|
cs.mu.Lock()
|
||||||
|
|
||||||
|
// 获取旧值
|
||||||
|
oldValue := cs.koanf.Get(key)
|
||||||
|
|
||||||
|
// 值未变化,直接返回
|
||||||
|
if reflect.DeepEqual(oldValue, value) {
|
||||||
|
cs.mu.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置新值
|
||||||
|
err := cs.koanf.Set(key, value)
|
||||||
|
if err != nil {
|
||||||
|
cs.mu.Unlock()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = cs.koanf.Set("metadata.lastUpdated", time.Now().Format(time.RFC3339))
|
||||||
|
if err != nil {
|
||||||
|
cs.mu.Unlock()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 写入文件
|
||||||
|
if err = cs.writeConfigToFile(); err != nil {
|
||||||
|
cs.mu.Unlock()
|
||||||
|
return fmt.Errorf("failed to write config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cs.mu.Unlock()
|
||||||
|
|
||||||
|
// 通知观察者
|
||||||
|
if cs.observer != nil {
|
||||||
|
cs.observer.Notify(key, oldValue, value)
|
||||||
|
} else {
|
||||||
|
cs.logger.Error("config observer is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetConfig 重置所有配置为默认值
|
||||||
func (cs *ConfigService) ResetConfig() error {
|
func (cs *ConfigService) ResetConfig() error {
|
||||||
cs.mu.Lock()
|
cs.mu.Lock()
|
||||||
|
|
||||||
// 保存旧配置快照
|
// 保存旧配置快照
|
||||||
oldSnapshot := cs.createConfigSnapshot()
|
oldSnapshot := cs.createSnapshot()
|
||||||
|
|
||||||
// 停止文件监听
|
// 重置为默认配置
|
||||||
if cs.fileProvider != nil {
|
cs.koanf = koanf.New(".")
|
||||||
cs.fileProvider.Unwatch()
|
defaultConfig := models.NewDefaultAppConfig()
|
||||||
cs.fileProvider = nil
|
if err := cs.koanf.Load(structs.Provider(defaultConfig, "json"), nil); err != nil {
|
||||||
}
|
|
||||||
|
|
||||||
// 设置默认配置
|
|
||||||
if err := cs.setDefaults(); err != nil {
|
|
||||||
cs.mu.Unlock()
|
cs.mu.Unlock()
|
||||||
return err
|
return fmt.Errorf("failed to load default config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 写入配置文件
|
// 写入配置文件
|
||||||
if err := cs.writeConfigToFile(); err != nil {
|
if err := cs.writeConfigToFile(); err != nil {
|
||||||
cs.mu.Unlock()
|
cs.mu.Unlock()
|
||||||
return err
|
return fmt.Errorf("failed to write config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 重新创建koanf实例
|
newSnapshot := cs.createSnapshot()
|
||||||
cs.koanf = koanf.New(".")
|
|
||||||
|
|
||||||
// 重新加载默认配置到koanf
|
|
||||||
if err := cs.setDefaults(); err != nil {
|
|
||||||
cs.mu.Unlock()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 重新创建文件提供器
|
|
||||||
cs.fileProvider = file.Provider(cs.settingsPath)
|
|
||||||
|
|
||||||
// 重新加载配置文件
|
|
||||||
if err := cs.koanf.Load(cs.fileProvider, jsonparser.Parser()); err != nil {
|
|
||||||
cs.mu.Unlock()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
newSnapshot := cs.createConfigSnapshot()
|
|
||||||
cs.mu.Unlock()
|
cs.mu.Unlock()
|
||||||
|
|
||||||
// 重新启动文件监听
|
// 通知配置变更
|
||||||
cs.startWatching()
|
|
||||||
|
|
||||||
// 检测配置变更并通知观察者
|
|
||||||
cs.notifyChanges(oldSnapshot, newSnapshot)
|
cs.notifyChanges(oldSnapshot, newSnapshot)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeConfigToFile 将配置写回JSON文件
|
// writeConfigToFile 将配置写入文件
|
||||||
func (cs *ConfigService) writeConfigToFile() error {
|
func (cs *ConfigService) writeConfigToFile() error {
|
||||||
configBytes, err := cs.koanf.Marshal(jsonparser.Parser())
|
configBytes, err := cs.koanf.Marshal(jsonparser.Parser())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
return os.WriteFile(cs.settingsPath, configBytes, 0644)
|
||||||
if err := os.WriteFile(cs.settingsPath, configBytes, 0644); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Watch 注册配置变更监听器
|
// Watch 注册配置变更监听器
|
||||||
func (cs *ConfigService) Watch(path string, callback ObserverCallback) CancelFunc {
|
func (cs *ConfigService) Watch(path string, callback helper.ObserverCallback) helper.CancelFunc {
|
||||||
return cs.observer.Watch(path, callback)
|
return cs.observer.Watch(path, callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WatchWithContext 使用 Context 注册监听器
|
// WatchWithContext 使用 Context 注册监听器
|
||||||
func (cs *ConfigService) WatchWithContext(ctx context.Context, path string, callback ObserverCallback) {
|
func (cs *ConfigService) WatchWithContext(ctx context.Context, path string, callback helper.ObserverCallback) {
|
||||||
cs.observer.WatchWithContext(ctx, path, callback)
|
cs.observer.WatchWithContext(ctx, path, callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
// createConfigSnapshot 创建当前配置的快照(调用者需确保已持有锁)
|
// createSnapshotLocked 创建配置快照
|
||||||
func (cs *ConfigService) createConfigSnapshot() map[string]interface{} {
|
func (cs *ConfigService) createSnapshot() map[string]interface{} {
|
||||||
snapshot := make(map[string]interface{})
|
snapshot := make(map[string]interface{})
|
||||||
allKeys := cs.koanf.All()
|
for _, key := range cs.koanf.Keys() {
|
||||||
flattenMap("", allKeys, snapshot)
|
snapshot[key] = cs.koanf.Get(key)
|
||||||
|
}
|
||||||
return snapshot
|
return snapshot
|
||||||
}
|
}
|
||||||
|
|
||||||
// flattenMap 递归展平嵌套的 map(使用 strings.Builder 优化字符串拼接)
|
|
||||||
func flattenMap(prefix string, data map[string]interface{}, result map[string]interface{}) {
|
|
||||||
var builder strings.Builder
|
|
||||||
for key, value := range data {
|
|
||||||
builder.Reset()
|
|
||||||
if prefix != "" {
|
|
||||||
builder.WriteString(prefix)
|
|
||||||
builder.WriteString(".")
|
|
||||||
}
|
|
||||||
builder.WriteString(key)
|
|
||||||
fullKey := builder.String()
|
|
||||||
|
|
||||||
if valueMap, ok := value.(map[string]interface{}); ok {
|
|
||||||
// 递归处理嵌套 map
|
|
||||||
flattenMap(fullKey, valueMap, result)
|
|
||||||
} else {
|
|
||||||
// 保存叶子节点
|
|
||||||
result[fullKey] = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// notifyChanges 检测配置变更并通知观察者
|
// notifyChanges 检测配置变更并通知观察者
|
||||||
func (cs *ConfigService) notifyChanges(oldSnapshot, newSnapshot map[string]interface{}) {
|
func (cs *ConfigService) notifyChanges(oldSnapshot, newSnapshot map[string]interface{}) {
|
||||||
// 检测变更
|
if cs.observer == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
changes := make(map[string]struct {
|
changes := make(map[string]struct {
|
||||||
OldValue interface{}
|
OldValue interface{}
|
||||||
NewValue interface{}
|
NewValue interface{}
|
||||||
@@ -345,14 +250,11 @@ func (cs *ConfigService) notifyChanges(oldSnapshot, newSnapshot map[string]inter
|
|||||||
// 检查新增和修改的键
|
// 检查新增和修改的键
|
||||||
for key, newValue := range newSnapshot {
|
for key, newValue := range newSnapshot {
|
||||||
oldValue, exists := oldSnapshot[key]
|
oldValue, exists := oldSnapshot[key]
|
||||||
if !exists || !isEqual(oldValue, newValue) {
|
if !exists || !reflect.DeepEqual(oldValue, newValue) {
|
||||||
changes[key] = struct {
|
changes[key] = struct {
|
||||||
OldValue interface{}
|
OldValue interface{}
|
||||||
NewValue interface{}
|
NewValue interface{}
|
||||||
}{
|
}{oldValue, newValue}
|
||||||
OldValue: oldValue,
|
|
||||||
NewValue: newValue,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -362,29 +264,18 @@ func (cs *ConfigService) notifyChanges(oldSnapshot, newSnapshot map[string]inter
|
|||||||
changes[key] = struct {
|
changes[key] = struct {
|
||||||
OldValue interface{}
|
OldValue interface{}
|
||||||
NewValue interface{}
|
NewValue interface{}
|
||||||
}{
|
}{oldValue, nil}
|
||||||
OldValue: oldValue,
|
|
||||||
NewValue: nil,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 通知所有变更
|
// 批量通知
|
||||||
if cs.observer != nil && len(changes) > 0 {
|
if len(changes) > 0 {
|
||||||
cs.observer.NotifyAll(changes)
|
cs.observer.NotifyAll(changes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// isEqual 值相等比较
|
|
||||||
func isEqual(a, b interface{}) bool {
|
|
||||||
aJSON, _ := json.Marshal(a)
|
|
||||||
bJSON, _ := json.Marshal(b)
|
|
||||||
return string(aJSON) == string(bJSON)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServiceShutdown 关闭服务
|
// ServiceShutdown 关闭服务
|
||||||
func (cs *ConfigService) ServiceShutdown() error {
|
func (cs *ConfigService) ServiceShutdown() error {
|
||||||
cs.stopWatching()
|
|
||||||
if cs.observer != nil {
|
if cs.observer != nil {
|
||||||
cs.observer.Shutdown()
|
cs.observer.Shutdown()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ type MigrationProgress struct {
|
|||||||
type MigrationService struct {
|
type MigrationService struct {
|
||||||
logger *log.LogService
|
logger *log.LogService
|
||||||
dbService *DatabaseService
|
dbService *DatabaseService
|
||||||
|
configService *ConfigService
|
||||||
progress atomic.Value // stores MigrationProgress
|
progress atomic.Value // stores MigrationProgress
|
||||||
|
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
@@ -34,13 +35,14 @@ type MigrationService struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewMigrationService 创建迁移服务
|
// NewMigrationService 创建迁移服务
|
||||||
func NewMigrationService(dbService *DatabaseService, logger *log.LogService) *MigrationService {
|
func NewMigrationService(dbService *DatabaseService, configService *ConfigService, logger *log.LogService) *MigrationService {
|
||||||
if logger == nil {
|
if logger == nil {
|
||||||
logger = log.New()
|
logger = log.New()
|
||||||
}
|
}
|
||||||
ms := &MigrationService{
|
ms := &MigrationService{
|
||||||
logger: logger,
|
logger: logger,
|
||||||
dbService: dbService,
|
dbService: dbService,
|
||||||
|
configService: configService,
|
||||||
}
|
}
|
||||||
ms.progress.Store(MigrationProgress{})
|
ms.progress.Store(MigrationProgress{})
|
||||||
return ms
|
return ms
|
||||||
@@ -94,9 +96,12 @@ func (ms *MigrationService) MigrateDirectory(srcPath, dstPath string) error {
|
|||||||
if err := ms.dbService.ServiceShutdown(); err != nil {
|
if err := ms.dbService.ServiceShutdown(); err != nil {
|
||||||
ms.logger.Error("Failed to close database connection", "error", err)
|
ms.logger.Error("Failed to close database connection", "error", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 等待文件句柄释放(Windows 特有问题)
|
||||||
|
time.Sleep(200 * time.Millisecond)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 确保失败时恢复数据库连接
|
// 确保恢复数据库连接
|
||||||
defer func() {
|
defer func() {
|
||||||
if ms.dbService != nil {
|
if ms.dbService != nil {
|
||||||
if err := ms.dbService.ServiceStartup(ctx, application.ServiceOptions{}); err != nil {
|
if err := ms.dbService.ServiceStartup(ctx, application.ServiceOptions{}); err != nil {
|
||||||
@@ -110,17 +115,59 @@ func (ms *MigrationService) MigrateDirectory(srcPath, dstPath string) error {
|
|||||||
return ms.fail(err)
|
return ms.fail(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 迁移成功后,立即更新配置到新路径
|
||||||
|
if ms.configService != nil {
|
||||||
|
if err := ms.configService.Set("general.dataPath", dstPath); err != nil {
|
||||||
|
return ms.fail(fmt.Errorf("migration succeeded but failed to update config: %w", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ms.setProgress(100)
|
ms.setProgress(100)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// preCheck 预检查,返回是否需要迁移
|
// preCheck 预检查,返回是否需要迁移
|
||||||
func (ms *MigrationService) preCheck(srcPath, dstPath string) (bool, error) {
|
func (ms *MigrationService) preCheck(srcPath, dstPath string) (bool, error) {
|
||||||
// 源目录不存在,无需迁移
|
// 检查源目录状态
|
||||||
if _, err := os.Stat(srcPath); os.IsNotExist(err) {
|
srcStat, srcErr := os.Stat(srcPath)
|
||||||
|
srcNotExist := os.IsNotExist(srcErr)
|
||||||
|
|
||||||
|
// 检查目标目录状态
|
||||||
|
dstStat, dstErr := os.Stat(dstPath)
|
||||||
|
dstNotExist := os.IsNotExist(dstErr)
|
||||||
|
|
||||||
|
// 1:源目录不存在
|
||||||
|
if srcNotExist {
|
||||||
|
// 如果目标目录存在且有内容,说明迁移已经完成
|
||||||
|
if !dstNotExist && dstStat.IsDir() {
|
||||||
|
isEmpty, err := isDirEmpty(dstPath)
|
||||||
|
if err == nil && !isEmpty {
|
||||||
|
ms.logger.Info("Migration already completed, source not exist but target has content", "dst", dstPath)
|
||||||
|
return false, nil // 无需迁移
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 源不存在且目标也不存在/为空,无需迁移
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 2. 源目录存在但为空
|
||||||
|
if srcStat.IsDir() {
|
||||||
|
srcEmpty, err := isDirEmpty(srcPath)
|
||||||
|
if err == nil && srcEmpty {
|
||||||
|
// 源为空,目标有内容 → 迁移已完成
|
||||||
|
if !dstNotExist && dstStat.IsDir() {
|
||||||
|
dstEmpty, _ := isDirEmpty(dstPath)
|
||||||
|
if !dstEmpty {
|
||||||
|
ms.logger.Info("Migration already completed, source is empty but target has content", "dst", dstPath)
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 源为空,目标也为空 → 无需迁移
|
||||||
|
ms.logger.Info("Both source and target are empty, no migration needed")
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 路径相同,无需迁移
|
// 路径相同,无需迁移
|
||||||
srcAbs, _ := filepath.Abs(srcPath)
|
srcAbs, _ := filepath.Abs(srcPath)
|
||||||
dstAbs, _ := filepath.Abs(dstPath)
|
dstAbs, _ := filepath.Abs(dstPath)
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
|
||||||
"voidraft/internal/common/helper"
|
"voidraft/internal/common/helper"
|
||||||
"voidraft/internal/common/hotkey"
|
"voidraft/internal/common/hotkey"
|
||||||
"voidraft/internal/models"
|
"voidraft/internal/models"
|
||||||
@@ -33,7 +32,7 @@ type HotkeyService struct {
|
|||||||
isShutdown atomic.Bool
|
isShutdown atomic.Bool
|
||||||
|
|
||||||
// 配置观察者取消函数
|
// 配置观察者取消函数
|
||||||
cancelObservers []CancelFunc
|
cancelObservers []helper.CancelFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHotkeyService 创建热键服务实例
|
// NewHotkeyService 创建热键服务实例
|
||||||
@@ -61,7 +60,7 @@ func (hs *HotkeyService) ServiceStartup(ctx context.Context, options application
|
|||||||
// Initialize 初始化热键服务
|
// Initialize 初始化热键服务
|
||||||
func (hs *HotkeyService) Initialize() error {
|
func (hs *HotkeyService) Initialize() error {
|
||||||
// 注册配置监听
|
// 注册配置监听
|
||||||
hs.cancelObservers = []CancelFunc{
|
hs.cancelObservers = []helper.CancelFunc{
|
||||||
hs.configService.Watch("general.enableGlobalHotkey", hs.onHotkeyConfigChange),
|
hs.configService.Watch("general.enableGlobalHotkey", hs.onHotkeyConfigChange),
|
||||||
hs.configService.Watch("general.globalHotkey", hs.onHotkeyConfigChange),
|
hs.configService.Watch("general.globalHotkey", hs.onHotkeyConfigChange),
|
||||||
}
|
}
|
||||||
@@ -84,11 +83,14 @@ func (hs *HotkeyService) onHotkeyConfigChange(oldValue, newValue interface{}) {
|
|||||||
// 重新加载配置
|
// 重新加载配置
|
||||||
config, err := hs.configService.GetConfig()
|
config, err := hs.configService.GetConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
hs.logger.Error("failed to get config", "error", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新热键
|
// 更新热键
|
||||||
_ = hs.UpdateHotkey(config.General.EnableGlobalHotkey, &config.General.GlobalHotkey)
|
if err := hs.UpdateHotkey(config.General.EnableGlobalHotkey, &config.General.GlobalHotkey); err != nil {
|
||||||
|
hs.logger.Error("failed to update hotkey", "error", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterHotkey 注册全局热键
|
// RegisterHotkey 注册全局热键
|
||||||
@@ -101,22 +103,34 @@ func (hs *HotkeyService) RegisterHotkey(combo *models.HotkeyCombo) error {
|
|||||||
return errors.New("invalid hotkey combination")
|
return errors.New("invalid hotkey combination")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果已注册,先取消
|
|
||||||
if hs.registered.Load() {
|
|
||||||
_ = hs.UnregisterHotkey()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 转换为 hotkey 库的格式
|
// 转换为 hotkey 库的格式
|
||||||
key, mods, err := hs.convertHotkey(combo)
|
key, mods, err := hs.convertHotkey(combo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("convert hotkey: %w", err)
|
return fmt.Errorf("convert hotkey: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
hs.mu.Lock()
|
// 如果已注册,异步清理旧热键
|
||||||
|
if hs.registered.Load() {
|
||||||
|
hs.mu.RLock()
|
||||||
|
oldHk := hs.hk
|
||||||
|
hs.mu.RUnlock()
|
||||||
|
|
||||||
|
if oldHk != nil {
|
||||||
|
// 异步清理,不阻塞当前流程
|
||||||
|
go func() {
|
||||||
|
if err := oldHk.Close(); err != nil {
|
||||||
|
hs.logger.Error("failed to close old hotkey (ignored)", "error", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 创建新的热键实例
|
// 创建新的热键实例
|
||||||
|
hs.mu.Lock()
|
||||||
hs.hk = hotkey.New(mods, key)
|
hs.hk = hotkey.New(mods, key)
|
||||||
if err := hs.hk.Register(); err != nil {
|
if err := hs.hk.Register(); err != nil {
|
||||||
hs.mu.Unlock()
|
hs.mu.Unlock()
|
||||||
|
hs.logger.Error("failed to register hotkey", "error", err)
|
||||||
return fmt.Errorf("register hotkey: %w", err)
|
return fmt.Errorf("register hotkey: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,34 +155,22 @@ func (hs *HotkeyService) UnregisterHotkey() error {
|
|||||||
hs.registered.Store(false)
|
hs.registered.Store(false)
|
||||||
|
|
||||||
// 获取热键实例的引用
|
// 获取热键实例的引用
|
||||||
hs.mu.RLock()
|
hs.mu.Lock()
|
||||||
hk := hs.hk
|
hk := hs.hk
|
||||||
hs.mu.RUnlock()
|
hs.hk = nil
|
||||||
|
hs.currentHotkey = nil
|
||||||
|
hs.mu.Unlock()
|
||||||
|
|
||||||
if hk == nil {
|
if hk == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 调用 Close() 确保完全清理
|
// 异步清理
|
||||||
_ = hk.Close()
|
|
||||||
|
|
||||||
// 等待监听 goroutine 退出
|
|
||||||
done := make(chan struct{})
|
|
||||||
go func() {
|
go func() {
|
||||||
hs.wg.Wait()
|
if err := hk.Close(); err != nil {
|
||||||
close(done)
|
hs.logger.Error("failed to close hotkey (ignored)", "error", err)
|
||||||
}()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-done:
|
|
||||||
case <-time.After(2 * time.Second):
|
|
||||||
}
|
}
|
||||||
|
}()
|
||||||
// 清理状态
|
|
||||||
hs.mu.Lock()
|
|
||||||
hs.hk = nil
|
|
||||||
hs.currentHotkey = nil
|
|
||||||
hs.mu.Unlock()
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -321,6 +323,24 @@ func (hs *HotkeyService) showAllWindows() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetSupportedKeys 返回系统支持的快捷键列表
|
||||||
|
func (hs *HotkeyService) GetSupportedKeys() []string {
|
||||||
|
// 返回当前系统支持的所有键
|
||||||
|
// 这个列表与 convertKey 方法中的 keyMap 保持一致
|
||||||
|
return []string{
|
||||||
|
// 字母键
|
||||||
|
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
|
||||||
|
"N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
|
||||||
|
// 数字键
|
||||||
|
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
|
||||||
|
// 功能键
|
||||||
|
"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12",
|
||||||
|
// 特殊键
|
||||||
|
"Space", "Tab", "Enter", "Escape", "Delete",
|
||||||
|
"ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// isValidHotkey 验证热键组合
|
// isValidHotkey 验证热键组合
|
||||||
func (hs *HotkeyService) isValidHotkey(combo *models.HotkeyCombo) bool {
|
func (hs *HotkeyService) isValidHotkey(combo *models.HotkeyCombo) bool {
|
||||||
if combo == nil || combo.Key == "" {
|
if combo == nil || combo.Key == "" {
|
||||||
@@ -333,24 +353,6 @@ func (hs *HotkeyService) isValidHotkey(combo *models.HotkeyCombo) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCurrentHotkey 获取当前热键
|
|
||||||
func (hs *HotkeyService) GetCurrentHotkey() *models.HotkeyCombo {
|
|
||||||
hs.mu.RLock()
|
|
||||||
defer hs.mu.RUnlock()
|
|
||||||
|
|
||||||
if hs.currentHotkey == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &models.HotkeyCombo{
|
|
||||||
Ctrl: hs.currentHotkey.Ctrl,
|
|
||||||
Shift: hs.currentHotkey.Shift,
|
|
||||||
Alt: hs.currentHotkey.Alt,
|
|
||||||
Win: hs.currentHotkey.Win,
|
|
||||||
Key: hs.currentHotkey.Key,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsRegistered 检查是否已注册
|
// IsRegistered 检查是否已注册
|
||||||
func (hs *HotkeyService) IsRegistered() bool {
|
func (hs *HotkeyService) IsRegistered() bool {
|
||||||
return hs.registered.Load()
|
return hs.registered.Load()
|
||||||
|
|||||||
@@ -1,387 +0,0 @@
|
|||||||
package services
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
"voidraft/internal/models"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v3/pkg/services/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestHotkeyServiceCreation 测试服务创建
|
|
||||||
func TestHotkeyServiceCreation(t *testing.T) {
|
|
||||||
logger := log.New()
|
|
||||||
configService := &ConfigService{} // Mock
|
|
||||||
|
|
||||||
service := NewHotkeyService(configService, logger)
|
|
||||||
if service == nil {
|
|
||||||
t.Fatal("Failed to create hotkey service")
|
|
||||||
}
|
|
||||||
|
|
||||||
if service.logger == nil {
|
|
||||||
t.Error("Logger should not be nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
if service.registered.Load() {
|
|
||||||
t.Error("Service should not have registered hotkey initially")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestHotkeyValidation 测试热键验证
|
|
||||||
func TestHotkeyValidation(t *testing.T) {
|
|
||||||
logger := log.New()
|
|
||||||
service := NewHotkeyService(&ConfigService{}, logger)
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
combo *models.HotkeyCombo
|
|
||||||
valid bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "Nil combo",
|
|
||||||
combo: nil,
|
|
||||||
valid: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Empty key",
|
|
||||||
combo: &models.HotkeyCombo{
|
|
||||||
Ctrl: true,
|
|
||||||
Key: "",
|
|
||||||
},
|
|
||||||
valid: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "No modifiers",
|
|
||||||
combo: &models.HotkeyCombo{
|
|
||||||
Key: "A",
|
|
||||||
},
|
|
||||||
valid: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Valid: Ctrl+A",
|
|
||||||
combo: &models.HotkeyCombo{
|
|
||||||
Ctrl: true,
|
|
||||||
Key: "A",
|
|
||||||
},
|
|
||||||
valid: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Valid: Ctrl+Shift+F1",
|
|
||||||
combo: &models.HotkeyCombo{
|
|
||||||
Ctrl: true,
|
|
||||||
Shift: true,
|
|
||||||
Key: "F1",
|
|
||||||
},
|
|
||||||
valid: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Valid: Alt+Space",
|
|
||||||
combo: &models.HotkeyCombo{
|
|
||||||
Alt: true,
|
|
||||||
Key: "Space",
|
|
||||||
},
|
|
||||||
valid: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
result := service.isValidHotkey(tt.combo)
|
|
||||||
if result != tt.valid {
|
|
||||||
t.Errorf("Expected valid=%v, got %v", tt.valid, result)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestHotkeyConversion 测试热键转换
|
|
||||||
func TestHotkeyConversion(t *testing.T) {
|
|
||||||
logger := log.New()
|
|
||||||
service := NewHotkeyService(&ConfigService{}, logger)
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
combo *models.HotkeyCombo
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "Valid letter key",
|
|
||||||
combo: &models.HotkeyCombo{
|
|
||||||
Ctrl: true,
|
|
||||||
Key: "A",
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Valid number key",
|
|
||||||
combo: &models.HotkeyCombo{
|
|
||||||
Shift: true,
|
|
||||||
Key: "1",
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Valid function key",
|
|
||||||
combo: &models.HotkeyCombo{
|
|
||||||
Alt: true,
|
|
||||||
Key: "F5",
|
|
||||||
},
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Invalid key",
|
|
||||||
combo: &models.HotkeyCombo{
|
|
||||||
Ctrl: true,
|
|
||||||
Key: "InvalidKey",
|
|
||||||
},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
key, mods, err := service.convertHotkey(tt.combo)
|
|
||||||
|
|
||||||
if tt.wantErr {
|
|
||||||
if err == nil {
|
|
||||||
t.Error("Expected error, got nil")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Unexpected error: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if key == 0 {
|
|
||||||
t.Error("Key should not be 0")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(mods) == 0 {
|
|
||||||
t.Error("Should have at least one modifier")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestHotkeyRegisterUnregister 测试注册和注销
|
|
||||||
func TestHotkeyRegisterUnregister(t *testing.T) {
|
|
||||||
logger := log.New()
|
|
||||||
service := NewHotkeyService(&ConfigService{}, logger)
|
|
||||||
|
|
||||||
combo := &models.HotkeyCombo{
|
|
||||||
Ctrl: true,
|
|
||||||
Shift: true,
|
|
||||||
Key: "F10",
|
|
||||||
}
|
|
||||||
|
|
||||||
// 测试注册
|
|
||||||
err := service.RegisterHotkey(combo)
|
|
||||||
if err != nil {
|
|
||||||
t.Logf("Register failed (may be expected in test environment): %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !service.IsRegistered() {
|
|
||||||
t.Error("Service should be registered")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证当前热键
|
|
||||||
current := service.GetCurrentHotkey()
|
|
||||||
if current == nil {
|
|
||||||
t.Error("Current hotkey should not be nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
if current.Key != combo.Key {
|
|
||||||
t.Errorf("Expected key %s, got %s", combo.Key, current.Key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 测试注销
|
|
||||||
err = service.UnregisterHotkey()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unregister failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if service.IsRegistered() {
|
|
||||||
t.Error("Service should not be registered after unregister")
|
|
||||||
}
|
|
||||||
|
|
||||||
current = service.GetCurrentHotkey()
|
|
||||||
if current != nil {
|
|
||||||
t.Error("Current hotkey should be nil after unregister")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestHotkeyUpdate 测试更新热键
|
|
||||||
func TestHotkeyUpdate(t *testing.T) {
|
|
||||||
logger := log.New()
|
|
||||||
service := NewHotkeyService(&ConfigService{}, logger)
|
|
||||||
|
|
||||||
combo1 := &models.HotkeyCombo{
|
|
||||||
Ctrl: true,
|
|
||||||
Key: "F11",
|
|
||||||
}
|
|
||||||
|
|
||||||
// 启用热键
|
|
||||||
err := service.UpdateHotkey(true, combo1)
|
|
||||||
if err != nil {
|
|
||||||
t.Logf("Update (enable) failed: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer service.UnregisterHotkey()
|
|
||||||
|
|
||||||
if !service.IsRegistered() {
|
|
||||||
t.Error("Should be registered after enable")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 禁用热键
|
|
||||||
err = service.UpdateHotkey(false, combo1)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Update (disable) failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if service.IsRegistered() {
|
|
||||||
t.Error("Should not be registered after disable")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestHotkeyDoubleRegister 测试重复注册
|
|
||||||
func TestHotkeyDoubleRegister(t *testing.T) {
|
|
||||||
logger := log.New()
|
|
||||||
service := NewHotkeyService(&ConfigService{}, logger)
|
|
||||||
|
|
||||||
combo := &models.HotkeyCombo{
|
|
||||||
Ctrl: true,
|
|
||||||
Alt: true,
|
|
||||||
Key: "F12",
|
|
||||||
}
|
|
||||||
|
|
||||||
err := service.RegisterHotkey(combo)
|
|
||||||
if err != nil {
|
|
||||||
t.Skip("First registration failed")
|
|
||||||
}
|
|
||||||
defer service.UnregisterHotkey()
|
|
||||||
|
|
||||||
// 第二次注册应该先取消第一次注册,然后重新注册
|
|
||||||
combo2 := &models.HotkeyCombo{
|
|
||||||
Shift: true,
|
|
||||||
Key: "F12",
|
|
||||||
}
|
|
||||||
|
|
||||||
err = service.RegisterHotkey(combo2)
|
|
||||||
if err != nil {
|
|
||||||
t.Logf("Second registration failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证当前热键是新的
|
|
||||||
current := service.GetCurrentHotkey()
|
|
||||||
if current != nil && current.Shift != combo2.Shift {
|
|
||||||
t.Error("Should have updated to new hotkey")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestHotkeyConcurrentAccess 测试并发访问
|
|
||||||
func TestHotkeyConcurrentAccess(t *testing.T) {
|
|
||||||
logger := log.New()
|
|
||||||
service := NewHotkeyService(&ConfigService{}, logger)
|
|
||||||
|
|
||||||
combo := &models.HotkeyCombo{
|
|
||||||
Ctrl: true,
|
|
||||||
Key: "G",
|
|
||||||
}
|
|
||||||
|
|
||||||
const goroutines = 10
|
|
||||||
done := make(chan bool, goroutines)
|
|
||||||
|
|
||||||
// 并发读取
|
|
||||||
for i := 0; i < goroutines; i++ {
|
|
||||||
go func() {
|
|
||||||
for j := 0; j < 100; j++ {
|
|
||||||
_ = service.IsRegistered()
|
|
||||||
_ = service.GetCurrentHotkey()
|
|
||||||
time.Sleep(time.Millisecond)
|
|
||||||
}
|
|
||||||
done <- true
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 主协程进行注册/注销操作
|
|
||||||
go func() {
|
|
||||||
for i := 0; i < 5; i++ {
|
|
||||||
service.RegisterHotkey(combo)
|
|
||||||
time.Sleep(50 * time.Millisecond)
|
|
||||||
service.UnregisterHotkey()
|
|
||||||
time.Sleep(50 * time.Millisecond)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// 等待所有 goroutine 完成
|
|
||||||
for i := 0; i < goroutines; i++ {
|
|
||||||
<-done
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Log("Concurrent access test completed without panics")
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestHotkeyServiceShutdown 测试服务关闭
|
|
||||||
func TestHotkeyServiceShutdown(t *testing.T) {
|
|
||||||
logger := log.New()
|
|
||||||
service := NewHotkeyService(&ConfigService{}, logger)
|
|
||||||
|
|
||||||
combo := &models.HotkeyCombo{
|
|
||||||
Ctrl: true,
|
|
||||||
Shift: true,
|
|
||||||
Key: "H",
|
|
||||||
}
|
|
||||||
|
|
||||||
err := service.RegisterHotkey(combo)
|
|
||||||
if err != nil {
|
|
||||||
t.Skip("Registration failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 测试 ServiceShutdown
|
|
||||||
err = service.ServiceShutdown()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("ServiceShutdown failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if service.IsRegistered() {
|
|
||||||
t.Error("Should not be registered after shutdown")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// BenchmarkHotkeyRegistration 基准测试:热键注册
|
|
||||||
func BenchmarkHotkeyRegistration(b *testing.B) {
|
|
||||||
logger := log.New()
|
|
||||||
service := NewHotkeyService(&ConfigService{}, logger)
|
|
||||||
|
|
||||||
combo := &models.HotkeyCombo{
|
|
||||||
Ctrl: true,
|
|
||||||
Key: "B",
|
|
||||||
}
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
service.RegisterHotkey(combo)
|
|
||||||
service.UnregisterHotkey()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// BenchmarkHotkeyConversion 基准测试:热键转换
|
|
||||||
func BenchmarkHotkeyConversion(b *testing.B) {
|
|
||||||
logger := log.New()
|
|
||||||
service := NewHotkeyService(&ConfigService{}, logger)
|
|
||||||
|
|
||||||
combo := &models.HotkeyCombo{
|
|
||||||
Ctrl: true,
|
|
||||||
Shift: true,
|
|
||||||
Alt: true,
|
|
||||||
Key: "F5",
|
|
||||||
}
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
service.convertHotkey(combo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"github.com/wailsapp/wails/v3/pkg/services/dock"
|
"github.com/wailsapp/wails/v3/pkg/services/dock"
|
||||||
"github.com/wailsapp/wails/v3/pkg/services/log"
|
"github.com/wailsapp/wails/v3/pkg/services/log"
|
||||||
"github.com/wailsapp/wails/v3/pkg/services/notifications"
|
"github.com/wailsapp/wails/v3/pkg/services/notifications"
|
||||||
|
"log/slog"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ServiceManager 服务管理器,负责协调各个服务
|
// ServiceManager 服务管理器,负责协调各个服务
|
||||||
@@ -36,7 +37,9 @@ type ServiceManager struct {
|
|||||||
// NewServiceManager 创建新的服务管理器实例
|
// NewServiceManager 创建新的服务管理器实例
|
||||||
func NewServiceManager() *ServiceManager {
|
func NewServiceManager() *ServiceManager {
|
||||||
// 初始化日志服务
|
// 初始化日志服务
|
||||||
logger := log.New()
|
logger := log.NewWithConfig(&log.Config{
|
||||||
|
LogLevel: slog.LevelDebug,
|
||||||
|
})
|
||||||
|
|
||||||
// 初始化badge服务
|
// 初始化badge服务
|
||||||
badgeService := dock.New()
|
badgeService := dock.New()
|
||||||
@@ -51,7 +54,7 @@ func NewServiceManager() *ServiceManager {
|
|||||||
databaseService := NewDatabaseService(configService, logger)
|
databaseService := NewDatabaseService(configService, logger)
|
||||||
|
|
||||||
// 初始化迁移服务
|
// 初始化迁移服务
|
||||||
migrationService := NewMigrationService(databaseService, logger)
|
migrationService := NewMigrationService(databaseService, configService, logger)
|
||||||
|
|
||||||
// 初始化文档服务
|
// 初始化文档服务
|
||||||
documentService := NewDocumentService(databaseService, logger)
|
documentService := NewDocumentService(databaseService, logger)
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ func (ws *WindowService) onWindowClosing(documentID int64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetOpenWindows 获取所有打开的文档窗口
|
// GetOpenWindows 获取所有打开的文档窗口
|
||||||
func (ws *WindowService) GetOpenWindows() []application.Window {
|
func (ws *WindowService) getOpenWindows() []application.Window {
|
||||||
app := application.Get()
|
app := application.Get()
|
||||||
return app.Window.GetAll()
|
return app.Window.GetAll()
|
||||||
}
|
}
|
||||||
@@ -130,7 +130,7 @@ func (ws *WindowService) IsDocumentWindowOpen(documentID int64) bool {
|
|||||||
func (ws *WindowService) ServiceShutdown() error {
|
func (ws *WindowService) ServiceShutdown() error {
|
||||||
// 从吸附服务中取消注册所有窗口
|
// 从吸附服务中取消注册所有窗口
|
||||||
if ws.windowSnapService != nil {
|
if ws.windowSnapService != nil {
|
||||||
windows := ws.GetOpenWindows()
|
windows := ws.getOpenWindows()
|
||||||
for _, window := range windows {
|
for _, window := range windows {
|
||||||
if documentID, err := strconv.ParseInt(window.Name(), 10, 64); err == nil {
|
if documentID, err := strconv.ParseInt(window.Name(), 10, 64); err == nil {
|
||||||
ws.windowSnapService.UnregisterWindow(documentID)
|
ws.windowSnapService.UnregisterWindow(documentID)
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ type WindowSnapService struct {
|
|||||||
windowMoveUnhooks map[int64]func() // documentID -> 子窗口移动监听清理函数
|
windowMoveUnhooks map[int64]func() // documentID -> 子窗口移动监听清理函数
|
||||||
|
|
||||||
// 配置观察者取消函数
|
// 配置观察者取消函数
|
||||||
cancelObserver CancelFunc
|
cancelObserver helper.CancelFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewWindowSnapService 创建新的窗口吸附服务实例
|
// NewWindowSnapService 创建新的窗口吸附服务实例
|
||||||
|
|||||||
Reference in New Issue
Block a user