electron-infra-kit 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (254) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +302 -0
  3. package/README.zh-CN.md +308 -0
  4. package/dist/core/error/WindowError.d.ts +56 -0
  5. package/dist/core/error/WindowError.js +71 -0
  6. package/dist/core/error/WindowError.js.map +1 -0
  7. package/dist/core/error/WindowError.mjs +71 -0
  8. package/dist/core/error/WindowError.mjs.map +1 -0
  9. package/dist/core/ipc/IpcHandler.d.ts +48 -0
  10. package/dist/core/ipc/IpcHandler.js +59 -0
  11. package/dist/core/ipc/IpcHandler.js.map +1 -0
  12. package/dist/core/ipc/IpcHandler.mjs +59 -0
  13. package/dist/core/ipc/IpcHandler.mjs.map +1 -0
  14. package/dist/core/ipc/IpcRouter.d.ts +70 -0
  15. package/dist/core/ipc/IpcRouter.js +143 -0
  16. package/dist/core/ipc/IpcRouter.js.map +1 -0
  17. package/dist/core/ipc/IpcRouter.mjs +143 -0
  18. package/dist/core/ipc/IpcRouter.mjs.map +1 -0
  19. package/dist/core/ipc/index.d.ts +3 -0
  20. package/dist/core/ipc/index.js +1 -0
  21. package/dist/core/ipc/index.js.map +1 -0
  22. package/dist/core/ipc/index.mjs +1 -0
  23. package/dist/core/ipc/index.mjs.map +1 -0
  24. package/dist/core/ipc/ipc-router.type.d.ts +73 -0
  25. package/dist/core/ipc/transport/index.d.ts +65 -0
  26. package/dist/core/ipc/transport/index.js +229 -0
  27. package/dist/core/ipc/transport/index.js.map +1 -0
  28. package/dist/core/ipc/transport/index.mjs +229 -0
  29. package/dist/core/ipc/transport/index.mjs.map +1 -0
  30. package/dist/core/ipc/transport/ipc.type.d.ts +36 -0
  31. package/dist/core/lifecycle/LifecycleManager.d.ts +66 -0
  32. package/dist/core/lifecycle/LifecycleManager.js +140 -0
  33. package/dist/core/lifecycle/LifecycleManager.js.map +1 -0
  34. package/dist/core/lifecycle/LifecycleManager.mjs +140 -0
  35. package/dist/core/lifecycle/LifecycleManager.mjs.map +1 -0
  36. package/dist/core/message-bus/MessageBus.d.ts +282 -0
  37. package/dist/core/message-bus/MessageBus.js +677 -0
  38. package/dist/core/message-bus/MessageBus.js.map +1 -0
  39. package/dist/core/message-bus/MessageBus.mjs +677 -0
  40. package/dist/core/message-bus/MessageBus.mjs.map +1 -0
  41. package/dist/core/message-bus/MessageBusClient.d.ts +100 -0
  42. package/dist/core/message-bus/MessageBusClient.js +280 -0
  43. package/dist/core/message-bus/MessageBusClient.js.map +1 -0
  44. package/dist/core/message-bus/MessageBusClient.mjs +280 -0
  45. package/dist/core/message-bus/MessageBusClient.mjs.map +1 -0
  46. package/dist/core/message-bus/core/DataStoreManager.d.ts +51 -0
  47. package/dist/core/message-bus/core/DataStoreManager.js +94 -0
  48. package/dist/core/message-bus/core/DataStoreManager.js.map +1 -0
  49. package/dist/core/message-bus/core/DataStoreManager.mjs +94 -0
  50. package/dist/core/message-bus/core/DataStoreManager.mjs.map +1 -0
  51. package/dist/core/message-bus/core/ManagedPort.d.ts +26 -0
  52. package/dist/core/message-bus/core/ManagedPort.js +55 -0
  53. package/dist/core/message-bus/core/ManagedPort.js.map +1 -0
  54. package/dist/core/message-bus/core/ManagedPort.mjs +55 -0
  55. package/dist/core/message-bus/core/ManagedPort.mjs.map +1 -0
  56. package/dist/core/message-bus/core/PortManager.d.ts +47 -0
  57. package/dist/core/message-bus/core/PortManager.js +114 -0
  58. package/dist/core/message-bus/core/PortManager.js.map +1 -0
  59. package/dist/core/message-bus/core/PortManager.mjs +114 -0
  60. package/dist/core/message-bus/core/PortManager.mjs.map +1 -0
  61. package/dist/core/message-bus/core/SubscriptionManager.d.ts +36 -0
  62. package/dist/core/message-bus/core/SubscriptionManager.js +78 -0
  63. package/dist/core/message-bus/core/SubscriptionManager.js.map +1 -0
  64. package/dist/core/message-bus/core/SubscriptionManager.mjs +78 -0
  65. package/dist/core/message-bus/core/SubscriptionManager.mjs.map +1 -0
  66. package/dist/core/message-bus/core/TransactionManager.d.ts +54 -0
  67. package/dist/core/message-bus/core/TransactionManager.js +95 -0
  68. package/dist/core/message-bus/core/TransactionManager.js.map +1 -0
  69. package/dist/core/message-bus/core/TransactionManager.mjs +95 -0
  70. package/dist/core/message-bus/core/TransactionManager.mjs.map +1 -0
  71. package/dist/core/message-bus/core/index.d.ts +5 -0
  72. package/dist/core/message-bus/index.d.ts +4 -0
  73. package/dist/core/message-bus/index.js +1 -0
  74. package/dist/core/message-bus/index.js.map +1 -0
  75. package/dist/core/message-bus/index.mjs +1 -0
  76. package/dist/core/message-bus/index.mjs.map +1 -0
  77. package/dist/core/message-bus/message-bus.type.d.ts +143 -0
  78. package/dist/core/message-bus/message-bus.type.js +26 -0
  79. package/dist/core/message-bus/message-bus.type.js.map +1 -0
  80. package/dist/core/message-bus/message-bus.type.mjs +26 -0
  81. package/dist/core/message-bus/message-bus.type.mjs.map +1 -0
  82. package/dist/core/message-bus/preload.d.ts +16 -0
  83. package/dist/core/message-bus/preload.js +27 -0
  84. package/dist/core/message-bus/preload.js.map +1 -0
  85. package/dist/core/message-bus/preload.mjs +27 -0
  86. package/dist/core/message-bus/preload.mjs.map +1 -0
  87. package/dist/core/message-bus/transport/ITransport.d.ts +40 -0
  88. package/dist/core/message-bus/transport/IpcTransport.d.ts +18 -0
  89. package/dist/core/message-bus/transport/IpcTransport.js +60 -0
  90. package/dist/core/message-bus/transport/IpcTransport.js.map +1 -0
  91. package/dist/core/message-bus/transport/IpcTransport.mjs +60 -0
  92. package/dist/core/message-bus/transport/IpcTransport.mjs.map +1 -0
  93. package/dist/core/message-bus/transport/MessagePortTransport.d.ts +15 -0
  94. package/dist/core/message-bus/transport/MessagePortTransport.js +35 -0
  95. package/dist/core/message-bus/transport/MessagePortTransport.js.map +1 -0
  96. package/dist/core/message-bus/transport/MessagePortTransport.mjs +35 -0
  97. package/dist/core/message-bus/transport/MessagePortTransport.mjs.map +1 -0
  98. package/dist/core/message-bus/transport/index.d.ts +3 -0
  99. package/dist/core/window/IpcSetup.d.ts +50 -0
  100. package/dist/core/window/IpcSetup.js +96 -0
  101. package/dist/core/window/IpcSetup.js.map +1 -0
  102. package/dist/core/window/IpcSetup.mjs +96 -0
  103. package/dist/core/window/IpcSetup.mjs.map +1 -0
  104. package/dist/core/window/WindowCreator.d.ts +66 -0
  105. package/dist/core/window/WindowCreator.js +168 -0
  106. package/dist/core/window/WindowCreator.js.map +1 -0
  107. package/dist/core/window/WindowCreator.mjs +168 -0
  108. package/dist/core/window/WindowCreator.mjs.map +1 -0
  109. package/dist/core/window/WindowManager.d.ts +214 -0
  110. package/dist/core/window/WindowManager.js +583 -0
  111. package/dist/core/window/WindowManager.js.map +1 -0
  112. package/dist/core/window/WindowManager.mjs +583 -0
  113. package/dist/core/window/WindowManager.mjs.map +1 -0
  114. package/dist/core/window/WindowStore.d.ts +136 -0
  115. package/dist/core/window/WindowStore.js +436 -0
  116. package/dist/core/window/WindowStore.js.map +1 -0
  117. package/dist/core/window/WindowStore.mjs +436 -0
  118. package/dist/core/window/WindowStore.mjs.map +1 -0
  119. package/dist/core/window/constants.d.ts +17 -0
  120. package/dist/core/window/constants.js +15 -0
  121. package/dist/core/window/constants.js.map +1 -0
  122. package/dist/core/window/constants.mjs +15 -0
  123. package/dist/core/window/constants.mjs.map +1 -0
  124. package/dist/core/window/core/MetricsManager.d.ts +14 -0
  125. package/dist/core/window/core/MetricsManager.js +27 -0
  126. package/dist/core/window/core/MetricsManager.js.map +1 -0
  127. package/dist/core/window/core/MetricsManager.mjs +27 -0
  128. package/dist/core/window/core/MetricsManager.mjs.map +1 -0
  129. package/dist/core/window/core/PluginExecutor.d.ts +22 -0
  130. package/dist/core/window/core/PluginExecutor.js +110 -0
  131. package/dist/core/window/core/PluginExecutor.js.map +1 -0
  132. package/dist/core/window/core/PluginExecutor.mjs +110 -0
  133. package/dist/core/window/core/PluginExecutor.mjs.map +1 -0
  134. package/dist/core/window/core/WindowContextManager.d.ts +26 -0
  135. package/dist/core/window/core/WindowContextManager.js +59 -0
  136. package/dist/core/window/core/WindowContextManager.js.map +1 -0
  137. package/dist/core/window/core/WindowContextManager.mjs +59 -0
  138. package/dist/core/window/core/WindowContextManager.mjs.map +1 -0
  139. package/dist/core/window/core/WindowLifecycle.d.ts +15 -0
  140. package/dist/core/window/core/WindowLifecycle.js +150 -0
  141. package/dist/core/window/core/WindowLifecycle.js.map +1 -0
  142. package/dist/core/window/core/WindowLifecycle.mjs +150 -0
  143. package/dist/core/window/core/WindowLifecycle.mjs.map +1 -0
  144. package/dist/core/window/core/WindowOperator.d.ts +90 -0
  145. package/dist/core/window/core/WindowOperator.js +154 -0
  146. package/dist/core/window/core/WindowOperator.js.map +1 -0
  147. package/dist/core/window/core/WindowOperator.mjs +154 -0
  148. package/dist/core/window/core/WindowOperator.mjs.map +1 -0
  149. package/dist/core/window/core/WindowRegistry.d.ts +168 -0
  150. package/dist/core/window/core/WindowRegistry.js +331 -0
  151. package/dist/core/window/core/WindowRegistry.js.map +1 -0
  152. package/dist/core/window/core/WindowRegistry.mjs +331 -0
  153. package/dist/core/window/core/WindowRegistry.mjs.map +1 -0
  154. package/dist/core/window/core/WindowStateManager.d.ts +40 -0
  155. package/dist/core/window/core/WindowStateManager.js +110 -0
  156. package/dist/core/window/core/WindowStateManager.js.map +1 -0
  157. package/dist/core/window/core/WindowStateManager.mjs +110 -0
  158. package/dist/core/window/core/WindowStateManager.mjs.map +1 -0
  159. package/dist/core/window/index.d.ts +7 -0
  160. package/dist/core/window/index.js +1 -0
  161. package/dist/core/window/index.js.map +1 -0
  162. package/dist/core/window/index.mjs +1 -0
  163. package/dist/core/window/index.mjs.map +1 -0
  164. package/dist/core/window/window-manager.schema.d.ts +50 -0
  165. package/dist/core/window/window-manager.schema.js +87 -0
  166. package/dist/core/window/window-manager.schema.js.map +1 -0
  167. package/dist/core/window/window-manager.schema.mjs +87 -0
  168. package/dist/core/window/window-manager.schema.mjs.map +1 -0
  169. package/dist/core/window/window-manager.type.d.ts +365 -0
  170. package/dist/index.d.ts +25 -0
  171. package/dist/index.js +33 -0
  172. package/dist/index.js.map +1 -0
  173. package/dist/index.mjs +33 -0
  174. package/dist/index.mjs.map +1 -0
  175. package/dist/index.umd.js +1 -0
  176. package/dist/infrastructure/config/ConfigManager.d.ts +133 -0
  177. package/dist/infrastructure/config/ConfigManager.js +218 -0
  178. package/dist/infrastructure/config/ConfigManager.js.map +1 -0
  179. package/dist/infrastructure/config/ConfigManager.mjs +218 -0
  180. package/dist/infrastructure/config/ConfigManager.mjs.map +1 -0
  181. package/dist/infrastructure/config/index.d.ts +1 -0
  182. package/dist/infrastructure/debug/EnhancedDebugHelper.d.ts +106 -0
  183. package/dist/infrastructure/debug/EnhancedDebugHelper.js +218 -0
  184. package/dist/infrastructure/debug/EnhancedDebugHelper.js.map +1 -0
  185. package/dist/infrastructure/debug/EnhancedDebugHelper.mjs +218 -0
  186. package/dist/infrastructure/debug/EnhancedDebugHelper.mjs.map +1 -0
  187. package/dist/infrastructure/debug/PerformanceMonitor.d.ts +45 -0
  188. package/dist/infrastructure/debug/PerformanceMonitor.js +67 -0
  189. package/dist/infrastructure/debug/PerformanceMonitor.js.map +1 -0
  190. package/dist/infrastructure/debug/PerformanceMonitor.mjs +67 -0
  191. package/dist/infrastructure/debug/PerformanceMonitor.mjs.map +1 -0
  192. package/dist/infrastructure/debug/index.d.ts +22 -0
  193. package/dist/infrastructure/debug/index.js +47 -0
  194. package/dist/infrastructure/debug/index.js.map +1 -0
  195. package/dist/infrastructure/debug/index.mjs +47 -0
  196. package/dist/infrastructure/debug/index.mjs.map +1 -0
  197. package/dist/infrastructure/errors/ErrorCodes.d.ts +74 -0
  198. package/dist/infrastructure/errors/ErrorCodes.js +78 -0
  199. package/dist/infrastructure/errors/ErrorCodes.js.map +1 -0
  200. package/dist/infrastructure/errors/ErrorCodes.mjs +78 -0
  201. package/dist/infrastructure/errors/ErrorCodes.mjs.map +1 -0
  202. package/dist/infrastructure/errors/StandardError.d.ts +61 -0
  203. package/dist/infrastructure/errors/StandardError.js +84 -0
  204. package/dist/infrastructure/errors/StandardError.js.map +1 -0
  205. package/dist/infrastructure/errors/StandardError.mjs +84 -0
  206. package/dist/infrastructure/errors/StandardError.mjs.map +1 -0
  207. package/dist/infrastructure/errors/index.d.ts +13 -0
  208. package/dist/infrastructure/errors/index.js +24 -0
  209. package/dist/infrastructure/errors/index.js.map +1 -0
  210. package/dist/infrastructure/errors/index.mjs +24 -0
  211. package/dist/infrastructure/errors/index.mjs.map +1 -0
  212. package/dist/infrastructure/logger/ElectronLogger.d.ts +39 -0
  213. package/dist/infrastructure/logger/ElectronLogger.js +65 -0
  214. package/dist/infrastructure/logger/ElectronLogger.js.map +1 -0
  215. package/dist/infrastructure/logger/ElectronLogger.mjs +65 -0
  216. package/dist/infrastructure/logger/ElectronLogger.mjs.map +1 -0
  217. package/dist/infrastructure/logger/index.d.ts +2 -0
  218. package/dist/infrastructure/logger/logger.type.d.ts +8 -0
  219. package/dist/internal/types/BrandedTypes.d.ts +64 -0
  220. package/dist/internal/types/BrandedTypes.js +54 -0
  221. package/dist/internal/types/BrandedTypes.js.map +1 -0
  222. package/dist/internal/types/BrandedTypes.mjs +54 -0
  223. package/dist/internal/types/BrandedTypes.mjs.map +1 -0
  224. package/dist/internal/types/PerformanceOptions.d.ts +87 -0
  225. package/dist/internal/types/branded.d.ts +42 -0
  226. package/dist/internal/utils/MessageDispatcher.d.ts +67 -0
  227. package/dist/internal/utils/MessageDispatcher.js +96 -0
  228. package/dist/internal/utils/MessageDispatcher.js.map +1 -0
  229. package/dist/internal/utils/MessageDispatcher.mjs +96 -0
  230. package/dist/internal/utils/MessageDispatcher.mjs.map +1 -0
  231. package/dist/internal/utils/RateLimiter.d.ts +41 -0
  232. package/dist/internal/utils/RateLimiter.js +83 -0
  233. package/dist/internal/utils/RateLimiter.js.map +1 -0
  234. package/dist/internal/utils/RateLimiter.mjs +83 -0
  235. package/dist/internal/utils/RateLimiter.mjs.map +1 -0
  236. package/dist/internal/utils/StateKeeper.d.ts +125 -0
  237. package/dist/internal/utils/StateKeeper.js +334 -0
  238. package/dist/internal/utils/StateKeeper.js.map +1 -0
  239. package/dist/internal/utils/StateKeeper.mjs +334 -0
  240. package/dist/internal/utils/StateKeeper.mjs.map +1 -0
  241. package/dist/internal/utils/branded-helpers.d.ts +33 -0
  242. package/dist/internal/utils/index.d.ts +5 -0
  243. package/dist/preload/index.d.ts +45 -0
  244. package/dist/preload/index.js +91 -0
  245. package/dist/preload/index.js.map +1 -0
  246. package/dist/preload/index.mjs +91 -0
  247. package/dist/preload/index.mjs.map +1 -0
  248. package/dist/preload/preload.type.d.ts +15 -0
  249. package/dist/types.d.ts +7 -0
  250. package/dist/types.js +1 -0
  251. package/dist/types.js.map +1 -0
  252. package/dist/types.mjs +1 -0
  253. package/dist/types.mjs.map +1 -0
  254. package/package.json +143 -0
@@ -0,0 +1,334 @@
1
+ 'use strict';var electron=require('electron'),fs=require('fs'),path=require('path'),crypto=require('crypto'),ElectronLogger=require('../../infrastructure/logger/ElectronLogger.js');function _interopNamespaceDefault(e){var n=Object.create(null);if(e){Object.keys(e).forEach(function(k){if(k!=='default'){var d=Object.getOwnPropertyDescriptor(e,k);Object.defineProperty(n,k,d.get?d:{enumerable:true,get:function(){return e[k]}});}})}n.default=e;return Object.freeze(n)}var fs__namespace=/*#__PURE__*/_interopNamespaceDefault(fs);var path__namespace=/*#__PURE__*/_interopNamespaceDefault(path);var crypto__namespace=/*#__PURE__*/_interopNamespaceDefault(crypto);/**
2
+ * StateKeeper.ts
3
+ * 窗口状态管理器,负责持久化和恢复 Electron 窗口的位置、大小等状态
4
+ *
5
+ * 主要功能:
6
+ * - 保存窗口状态到文件系统
7
+ * - 从文件系统加载窗口状态
8
+ * - 验证窗口状态的有效性(确保窗口在可用显示器范围内)
9
+ * - 支持延迟保存机制,避免频繁 IO 操作
10
+ */
11
+ /**
12
+ * 简单的深度对比函数,用于检查窗口状态是否发生变化
13
+ */
14
+ function isStateEqual(a, b) {
15
+ if (a === b)
16
+ return true;
17
+ if (!a || !b)
18
+ return false;
19
+ // Check groups equality
20
+ const groupsA = a.groups || [];
21
+ const groupsB = b.groups || [];
22
+ const groupsEqual = groupsA.length === groupsB.length &&
23
+ [...groupsA].sort().every((val, index) => val === [...groupsB].sort()[index]);
24
+ return (a.x === b.x &&
25
+ a.y === b.y &&
26
+ a.width === b.width &&
27
+ a.height === b.height &&
28
+ a.isMaximized === b.isMaximized &&
29
+ a.isFullScreen === b.isFullScreen &&
30
+ groupsEqual &&
31
+ // 比较 displayBounds
32
+ ((!a.displayBounds && !b.displayBounds) ||
33
+ (!!a.displayBounds &&
34
+ !!b.displayBounds &&
35
+ a.displayBounds.x === b.displayBounds.x &&
36
+ a.displayBounds.y === b.displayBounds.y &&
37
+ a.displayBounds.width === b.displayBounds.width &&
38
+ a.displayBounds.height === b.displayBounds.height)));
39
+ }
40
+ /**
41
+ * 窗口状态管理器类
42
+ * 实现了窗口状态的持久化存储和加载功能
43
+ */
44
+ class StateKeeper {
45
+ stateFile; // 状态文件路径
46
+ state = {}; // 存储所有窗口的状态
47
+ logger; // 日志记录器
48
+ saveTimer = null; // 延迟保存计时器
49
+ throttleLastSave = 0; // 节流模式下上次保存时间
50
+ saveDelay; // 延迟保存时间(毫秒)
51
+ saveStrategy; // 保存策略
52
+ // Track active state files to prevent multiple instances writing to the same file
53
+ // 跟踪活动的 State 文件,防止多个实例写入同一个文件
54
+ static activeFiles = new Set();
55
+ hasLock = false;
56
+ isWriting = false;
57
+ pendingWrite = false;
58
+ lastSavedHash = '';
59
+ /**
60
+ * 构造函数
61
+ * @param options 配置选项
62
+ */
63
+ constructor(options = {}) {
64
+ // 初始化日志记录器
65
+ this.logger = options.logger || new ElectronLogger.ElectronLogger({ appName: 'StateKeeper' });
66
+ // 初始化配置
67
+ this.saveDelay = options.saveDelay ?? 500;
68
+ this.saveStrategy = options.saveStrategy ?? 'debounce';
69
+ // 获取应用程序的数据目录
70
+ const userDataDir = electron.app.getPath('userData');
71
+ // 设置状态文件路径
72
+ this.stateFile = options.stateFilePath || path__namespace.join(userDataDir, 'window-state.json');
73
+ // Check if file is already being used by another instance
74
+ // 检查文件是否已被另一个实例使用
75
+ if (StateKeeper.activeFiles.has(this.stateFile)) {
76
+ this.logger.warn(`State file "${this.stateFile}" is already in use by another StateKeeper instance. This may lead to data conflicts.`);
77
+ this.hasLock = false;
78
+ }
79
+ else {
80
+ StateKeeper.activeFiles.add(this.stateFile);
81
+ this.hasLock = true;
82
+ }
83
+ // 加载已保存的窗口状态
84
+ this.loadState();
85
+ // 监听 app 退出事件,强制保存
86
+ electron.app.on('before-quit', () => {
87
+ this.flushSync();
88
+ });
89
+ }
90
+ /**
91
+ * 从文件加载窗口状态
92
+ * 私有方法,仅在构造函数中调用
93
+ */
94
+ loadState() {
95
+ try {
96
+ // 检查状态文件是否存在
97
+ if (fs__namespace.existsSync(this.stateFile)) {
98
+ // 读取文件内容
99
+ const data = fs__namespace.readFileSync(this.stateFile, 'utf-8');
100
+ // 解析 JSON 数据到状态对象
101
+ this.state = JSON.parse(data);
102
+ // Calculate initial hash
103
+ this.lastSavedHash = crypto__namespace.createHash('md5').update(data).digest('hex');
104
+ }
105
+ }
106
+ catch (error) {
107
+ // 加载失败时记录错误并初始化空状态
108
+ this.logger.error(`Failed to load window state: ${error}`);
109
+ this.state = {};
110
+ }
111
+ }
112
+ /**
113
+ * 保存指定窗口的状态
114
+ * @param windowName 窗口名称,用于标识不同窗口的状态
115
+ * @param windowState 窗口状态对象
116
+ */
117
+ saveState(windowName, windowState) {
118
+ // 1. Dirty Check (脏检查)
119
+ // 如果新状态与当前保存的状态一致,则跳过保存
120
+ const currentState = this.state[windowName];
121
+ if (isStateEqual(currentState, windowState)) {
122
+ return;
123
+ }
124
+ // 更新内存中的窗口状态
125
+ this.state[windowName] = windowState;
126
+ // 触发持久化操作(带延迟)
127
+ this.persist();
128
+ }
129
+ /**
130
+ * 立即同步保存状态(不使用延迟)
131
+ */
132
+ flushSync() {
133
+ if (this.saveTimer) {
134
+ clearTimeout(this.saveTimer);
135
+ this.saveTimer = null;
136
+ }
137
+ try {
138
+ fs__namespace.writeFileSync(this.stateFile, JSON.stringify(this.state, null, 2));
139
+ }
140
+ catch (error) {
141
+ this.logger.error(`Failed to flush window state: ${error}`);
142
+ }
143
+ }
144
+ /**
145
+ * 将内存中的状态持久化到文件
146
+ * 私有方法,使用延迟保存机制避免频繁 IO 操作
147
+ */
148
+ persist() {
149
+ if (this.saveStrategy === 'throttle') {
150
+ // 节流模式:在指定时间内只保存一次
151
+ const now = Date.now();
152
+ const timeSinceLastSave = now - this.throttleLastSave;
153
+ if (timeSinceLastSave >= this.saveDelay) {
154
+ // 立即保存
155
+ this.performSave();
156
+ this.throttleLastSave = now;
157
+ }
158
+ else {
159
+ // 安排在剩余时间后保存
160
+ if (this.saveTimer) {
161
+ clearTimeout(this.saveTimer);
162
+ }
163
+ this.saveTimer = setTimeout(() => {
164
+ this.performSave();
165
+ this.throttleLastSave = Date.now();
166
+ }, this.saveDelay - timeSinceLastSave);
167
+ }
168
+ }
169
+ else {
170
+ // 防抖模式(默认):重置计时器
171
+ if (this.saveTimer) {
172
+ clearTimeout(this.saveTimer);
173
+ }
174
+ this.saveTimer = setTimeout(() => {
175
+ this.performSave();
176
+ }, this.saveDelay);
177
+ }
178
+ }
179
+ /**
180
+ * 执行实际的保存操作
181
+ * 私有方法
182
+ */
183
+ async performSave() {
184
+ // If already writing, mark pending and return
185
+ // 如果正在写入,标记为等待写入并返回
186
+ if (this.isWriting) {
187
+ this.pendingWrite = true;
188
+ return;
189
+ }
190
+ this.isWriting = true;
191
+ const tempFile = `${this.stateFile}.tmp`; // 临时文件路径
192
+ try {
193
+ const data = JSON.stringify(this.state, null, 2);
194
+ // Optimization: Check if data has actually changed using MD5 hash
195
+ // 优化:使用 MD5 哈希检查数据是否实际发生变化
196
+ const currentHash = crypto__namespace.createHash('md5').update(data).digest('hex');
197
+ if (currentHash === this.lastSavedHash) {
198
+ // No changes, skip write
199
+ this.isWriting = false;
200
+ return;
201
+ }
202
+ // 1. Write to temporary file first (Atomic Write Step 1)
203
+ // 先写入临时文件(原子写入步骤 1)
204
+ await fs__namespace.promises.writeFile(tempFile, data);
205
+ // 2. Rename temporary file to actual file (Atomic Write Step 2)
206
+ // 重命名临时文件为实际文件(原子写入步骤 2)
207
+ // This operation is atomic on most file systems
208
+ // 此操作在大多数文件系统上是原子的
209
+ await fs__namespace.promises.rename(tempFile, this.stateFile);
210
+ this.lastSavedHash = currentHash;
211
+ }
212
+ catch (error) {
213
+ this.logger.error(`Failed to save window state: ${error}`);
214
+ // Cleanup: Try to delete the temp file if it exists
215
+ // 清理:如果临时文件存在,尝试删除
216
+ try {
217
+ await fs__namespace.promises.unlink(tempFile);
218
+ }
219
+ catch (cleanupError) {
220
+ // Ignore cleanup errors (e.g. file not found)
221
+ // 忽略清理错误(例如文件未找到)
222
+ }
223
+ }
224
+ finally {
225
+ this.saveTimer = null;
226
+ this.isWriting = false;
227
+ // If a write was requested while writing, trigger another save immediately
228
+ // 如果在写入期间请求了写入,立即触发另一次保存
229
+ if (this.pendingWrite) {
230
+ this.pendingWrite = false;
231
+ // Use setImmediate to break the call stack and allow other events
232
+ // 使用 setImmediate 打断调用栈并允许处理其他事件
233
+ setImmediate(() => this.performSave());
234
+ }
235
+ }
236
+ }
237
+ /**
238
+ * 获取指定窗口的状态
239
+ * @param windowName 窗口名称
240
+ * @param defaultWidth 默认宽度,当没有保存状态或状态无效时使用
241
+ * @param defaultHeight 默认高度,当没有保存状态或状态无效时使用
242
+ * @returns 窗口状态对象
243
+ */
244
+ getWindowState(windowName, defaultWidth = 800, defaultHeight = 600) {
245
+ // 获取保存的窗口状态
246
+ const savedState = this.state[windowName];
247
+ // 如果没有保存的状态,返回默认状态
248
+ if (!savedState) {
249
+ return {
250
+ width: defaultWidth,
251
+ height: defaultHeight,
252
+ isMaximized: false,
253
+ isFullScreen: false,
254
+ };
255
+ }
256
+ // 验证保存的状态是否有效
257
+ if (!this.isValidState(savedState)) {
258
+ // 如果状态无效,返回默认状态
259
+ return {
260
+ width: defaultWidth,
261
+ height: defaultHeight,
262
+ isMaximized: false,
263
+ isFullScreen: false,
264
+ };
265
+ }
266
+ // 返回有效的保存状态
267
+ return savedState;
268
+ }
269
+ /**
270
+ * 验证窗口状态是否有效
271
+ * 主要检查窗口是否在当前可用显示器范围内
272
+ * @param state 要验证的窗口状态
273
+ * @returns 状态是否有效
274
+ */
275
+ isValidState(state) {
276
+ // 获取所有可用显示器
277
+ const displays = electron.screen.getAllDisplays();
278
+ // 优先检查保存的显示器信息是否与当前显示器匹配
279
+ if (state.displayBounds) {
280
+ const displayMatch = displays.find((d) => {
281
+ // 精确匹配显示器边界
282
+ return (d.bounds.x === state.displayBounds?.x &&
283
+ d.bounds.y === state.displayBounds?.y &&
284
+ d.bounds.width === state.displayBounds?.width &&
285
+ d.bounds.height === state.displayBounds?.height);
286
+ });
287
+ if (displayMatch) {
288
+ // 如果找到完全匹配的显示器,并且窗口有坐标信息,则认为状态有效
289
+ if (state.x !== undefined && state.y !== undefined) {
290
+ return true;
291
+ }
292
+ }
293
+ }
294
+ // 降级验证:检查窗口是否与任何显示器相交
295
+ const isVisible = displays.some((display) => {
296
+ const { x, y, width, height } = display.bounds;
297
+ // 如果窗口没有坐标信息,使用默认值 0
298
+ const wx = state.x || 0;
299
+ const wy = state.y || 0;
300
+ const ww = state.width;
301
+ const wh = state.height;
302
+ // 简单的矩形相交检查
303
+ return wx < x + width && wx + ww > x && wy < y + height && wy + wh > y;
304
+ });
305
+ return isVisible;
306
+ }
307
+ /**
308
+ * 移除指定窗口的状态
309
+ * @param windowName 窗口名称
310
+ */
311
+ removeState(windowName) {
312
+ // 检查是否存在该窗口的状态
313
+ if (this.state[windowName]) {
314
+ // 从内存中删除窗口状态
315
+ delete this.state[windowName];
316
+ // 触发持久化操作
317
+ this.persist();
318
+ }
319
+ }
320
+ /**
321
+ * Dispose StateKeeper instance
322
+ * 释放 StateKeeper 实例
323
+ */
324
+ dispose() {
325
+ if (this.saveTimer) {
326
+ clearTimeout(this.saveTimer);
327
+ this.saveTimer = null;
328
+ }
329
+ // Only remove from activeFiles if this instance acquired the lock
330
+ if (this.hasLock) {
331
+ StateKeeper.activeFiles.delete(this.stateFile);
332
+ }
333
+ }
334
+ }module.exports=StateKeeper;//# sourceMappingURL=StateKeeper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StateKeeper.js","sources":["../../../src/internal/utils/StateKeeper.ts"],"sourcesContent":["/**\n * StateKeeper.ts\n * 窗口状态管理器,负责持久化和恢复 Electron 窗口的位置、大小等状态\n *\n * 主要功能:\n * - 保存窗口状态到文件系统\n * - 从文件系统加载窗口状态\n * - 验证窗口状态的有效性(确保窗口在可用显示器范围内)\n * - 支持延迟保存机制,避免频繁 IO 操作\n */\n\nimport { app, screen } from 'electron';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as crypto from 'crypto';\nimport { Logger, ILogger } from '@/infrastructure/logger';\n\n/**\n * 窗口状态接口,定义了需要持久化的窗口属性\n */\nexport interface WindowState {\n x?: number; // 窗口左上角 X 坐标\n y?: number; // 窗口左上角 Y 坐标\n width: number; // 窗口宽度\n height: number; // 窗口高度\n isMaximized: boolean; // 是否最大化\n isFullScreen: boolean; // 是否全屏\n displayBounds?: Electron.Rectangle; // 显示该窗口的显示器边界信息\n groups?: string[]; // 窗口所属的组\n}\n\n/**\n * StateKeeper Configuration Options\n * StateKeeper 配置选项\n */\nexport interface StateKeeperOptions {\n /**\n * Delay before saving state to disk (in milliseconds)\n * 保存状态到磁盘前的延迟时间(毫秒)\n * @default 500\n */\n saveDelay?: number;\n\n /**\n * Save strategy: 'debounce' or 'throttle'\n * 保存策略:'debounce'(防抖)或 'throttle'(节流)\n * @default 'debounce'\n */\n saveStrategy?: 'debounce' | 'throttle';\n\n /**\n * Custom logger instance\n * 自定义日志实例\n */\n logger?: ILogger;\n\n /**\n * Custom state file path\n * 自定义状态文件路径\n */\n stateFilePath?: string;\n}\n\n/**\n * 简单的深度对比函数,用于检查窗口状态是否发生变化\n */\nfunction isStateEqual(a: WindowState | undefined, b: WindowState | undefined): boolean {\n if (a === b) return true;\n if (!a || !b) return false;\n\n // Check groups equality\n const groupsA = a.groups || [];\n const groupsB = b.groups || [];\n const groupsEqual =\n groupsA.length === groupsB.length &&\n [...groupsA].sort().every((val, index) => val === [...groupsB].sort()[index]);\n\n return (\n a.x === b.x &&\n a.y === b.y &&\n a.width === b.width &&\n a.height === b.height &&\n a.isMaximized === b.isMaximized &&\n a.isFullScreen === b.isFullScreen &&\n groupsEqual &&\n // 比较 displayBounds\n ((!a.displayBounds && !b.displayBounds) ||\n (!!a.displayBounds &&\n !!b.displayBounds &&\n a.displayBounds.x === b.displayBounds.x &&\n a.displayBounds.y === b.displayBounds.y &&\n a.displayBounds.width === b.displayBounds.width &&\n a.displayBounds.height === b.displayBounds.height))\n );\n}\n\n/**\n * 窗口状态管理器类\n * 实现了窗口状态的持久化存储和加载功能\n */\nexport default class StateKeeper {\n private stateFile: string; // 状态文件路径\n private state: Record<string, WindowState> = {}; // 存储所有窗口的状态\n private logger: ILogger; // 日志记录器\n\n private saveTimer: NodeJS.Timeout | null = null; // 延迟保存计时器\n private throttleLastSave: number = 0; // 节流模式下上次保存时间\n private readonly saveDelay: number; // 延迟保存时间(毫秒)\n private readonly saveStrategy: 'debounce' | 'throttle'; // 保存策略\n\n // Track active state files to prevent multiple instances writing to the same file\n // 跟踪活动的 State 文件,防止多个实例写入同一个文件\n private static activeFiles: Set<string> = new Set();\n private hasLock: boolean = false;\n private isWriting: boolean = false;\n private pendingWrite: boolean = false;\n private lastSavedHash: string = '';\n\n /**\n * 构造函数\n * @param options 配置选项\n */\n constructor(options: StateKeeperOptions = {}) {\n // 初始化日志记录器\n this.logger = options.logger || new Logger({ appName: 'StateKeeper' });\n\n // 初始化配置\n this.saveDelay = options.saveDelay ?? 500;\n this.saveStrategy = options.saveStrategy ?? 'debounce';\n\n // 获取应用程序的数据目录\n const userDataDir = app.getPath('userData');\n // 设置状态文件路径\n this.stateFile = options.stateFilePath || path.join(userDataDir, 'window-state.json');\n\n // Check if file is already being used by another instance\n // 检查文件是否已被另一个实例使用\n if (StateKeeper.activeFiles.has(this.stateFile)) {\n this.logger.warn(\n `State file \"${this.stateFile}\" is already in use by another StateKeeper instance. This may lead to data conflicts.`\n );\n this.hasLock = false;\n } else {\n StateKeeper.activeFiles.add(this.stateFile);\n this.hasLock = true;\n }\n\n // 加载已保存的窗口状态\n this.loadState();\n\n // 监听 app 退出事件,强制保存\n app.on('before-quit', () => {\n this.flushSync();\n });\n }\n\n /**\n * 从文件加载窗口状态\n * 私有方法,仅在构造函数中调用\n */\n private loadState(): void {\n try {\n // 检查状态文件是否存在\n if (fs.existsSync(this.stateFile)) {\n // 读取文件内容\n const data = fs.readFileSync(this.stateFile, 'utf-8');\n // 解析 JSON 数据到状态对象\n this.state = JSON.parse(data);\n // Calculate initial hash\n this.lastSavedHash = crypto.createHash('md5').update(data).digest('hex');\n }\n } catch (error) {\n // 加载失败时记录错误并初始化空状态\n this.logger.error(`Failed to load window state: ${error}`);\n this.state = {};\n }\n }\n\n /**\n * 保存指定窗口的状态\n * @param windowName 窗口名称,用于标识不同窗口的状态\n * @param windowState 窗口状态对象\n */\n public saveState(windowName: string, windowState: WindowState): void {\n // 1. Dirty Check (脏检查)\n // 如果新状态与当前保存的状态一致,则跳过保存\n const currentState = this.state[windowName];\n if (isStateEqual(currentState, windowState)) {\n return;\n }\n\n // 更新内存中的窗口状态\n this.state[windowName] = windowState;\n // 触发持久化操作(带延迟)\n this.persist();\n }\n\n /**\n * 立即同步保存状态(不使用延迟)\n */\n public flushSync(): void {\n if (this.saveTimer) {\n clearTimeout(this.saveTimer);\n this.saveTimer = null;\n }\n\n try {\n fs.writeFileSync(this.stateFile, JSON.stringify(this.state, null, 2));\n } catch (error) {\n this.logger.error(`Failed to flush window state: ${error}`);\n }\n }\n\n /**\n * 将内存中的状态持久化到文件\n * 私有方法,使用延迟保存机制避免频繁 IO 操作\n */\n private persist(): void {\n if (this.saveStrategy === 'throttle') {\n // 节流模式:在指定时间内只保存一次\n const now = Date.now();\n const timeSinceLastSave = now - this.throttleLastSave;\n\n if (timeSinceLastSave >= this.saveDelay) {\n // 立即保存\n this.performSave();\n this.throttleLastSave = now;\n } else {\n // 安排在剩余时间后保存\n if (this.saveTimer) {\n clearTimeout(this.saveTimer);\n }\n this.saveTimer = setTimeout(() => {\n this.performSave();\n this.throttleLastSave = Date.now();\n }, this.saveDelay - timeSinceLastSave);\n }\n } else {\n // 防抖模式(默认):重置计时器\n if (this.saveTimer) {\n clearTimeout(this.saveTimer);\n }\n\n this.saveTimer = setTimeout(() => {\n this.performSave();\n }, this.saveDelay);\n }\n }\n\n /**\n * 执行实际的保存操作\n * 私有方法\n */\n private async performSave(): Promise<void> {\n // If already writing, mark pending and return\n // 如果正在写入,标记为等待写入并返回\n if (this.isWriting) {\n this.pendingWrite = true;\n return;\n }\n\n this.isWriting = true;\n const tempFile = `${this.stateFile}.tmp`; // 临时文件路径\n\n try {\n const data = JSON.stringify(this.state, null, 2);\n\n // Optimization: Check if data has actually changed using MD5 hash\n // 优化:使用 MD5 哈希检查数据是否实际发生变化\n const currentHash = crypto.createHash('md5').update(data).digest('hex');\n if (currentHash === this.lastSavedHash) {\n // No changes, skip write\n this.isWriting = false;\n return;\n }\n\n // 1. Write to temporary file first (Atomic Write Step 1)\n // 先写入临时文件(原子写入步骤 1)\n await fs.promises.writeFile(tempFile, data);\n\n // 2. Rename temporary file to actual file (Atomic Write Step 2)\n // 重命名临时文件为实际文件(原子写入步骤 2)\n // This operation is atomic on most file systems\n // 此操作在大多数文件系统上是原子的\n await fs.promises.rename(tempFile, this.stateFile);\n\n this.lastSavedHash = currentHash;\n } catch (error) {\n this.logger.error(`Failed to save window state: ${error}`);\n\n // Cleanup: Try to delete the temp file if it exists\n // 清理:如果临时文件存在,尝试删除\n try {\n await fs.promises.unlink(tempFile);\n } catch (cleanupError) {\n // Ignore cleanup errors (e.g. file not found)\n // 忽略清理错误(例如文件未找到)\n }\n } finally {\n this.saveTimer = null;\n this.isWriting = false;\n\n // If a write was requested while writing, trigger another save immediately\n // 如果在写入期间请求了写入,立即触发另一次保存\n if (this.pendingWrite) {\n this.pendingWrite = false;\n // Use setImmediate to break the call stack and allow other events\n // 使用 setImmediate 打断调用栈并允许处理其他事件\n setImmediate(() => this.performSave());\n }\n }\n }\n\n /**\n * 获取指定窗口的状态\n * @param windowName 窗口名称\n * @param defaultWidth 默认宽度,当没有保存状态或状态无效时使用\n * @param defaultHeight 默认高度,当没有保存状态或状态无效时使用\n * @returns 窗口状态对象\n */\n public getWindowState(\n windowName: string,\n defaultWidth: number = 800,\n defaultHeight: number = 600\n ): WindowState {\n // 获取保存的窗口状态\n const savedState = this.state[windowName];\n // 如果没有保存的状态,返回默认状态\n if (!savedState) {\n return {\n width: defaultWidth,\n height: defaultHeight,\n isMaximized: false,\n isFullScreen: false,\n };\n }\n\n // 验证保存的状态是否有效\n if (!this.isValidState(savedState)) {\n // 如果状态无效,返回默认状态\n return {\n width: defaultWidth,\n height: defaultHeight,\n isMaximized: false,\n isFullScreen: false,\n };\n }\n\n // 返回有效的保存状态\n return savedState;\n }\n\n /**\n * 验证窗口状态是否有效\n * 主要检查窗口是否在当前可用显示器范围内\n * @param state 要验证的窗口状态\n * @returns 状态是否有效\n */\n private isValidState(state: WindowState): boolean {\n // 获取所有可用显示器\n const displays = screen.getAllDisplays();\n\n // 优先检查保存的显示器信息是否与当前显示器匹配\n if (state.displayBounds) {\n const displayMatch = displays.find((d) => {\n // 精确匹配显示器边界\n return (\n d.bounds.x === state.displayBounds?.x &&\n d.bounds.y === state.displayBounds?.y &&\n d.bounds.width === state.displayBounds?.width &&\n d.bounds.height === state.displayBounds?.height\n );\n });\n\n if (displayMatch) {\n // 如果找到完全匹配的显示器,并且窗口有坐标信息,则认为状态有效\n if (state.x !== undefined && state.y !== undefined) {\n return true;\n }\n }\n }\n\n // 降级验证:检查窗口是否与任何显示器相交\n const isVisible = displays.some((display) => {\n const { x, y, width, height } = display.bounds;\n // 如果窗口没有坐标信息,使用默认值 0\n const wx = state.x || 0;\n const wy = state.y || 0;\n const ww = state.width;\n const wh = state.height;\n\n // 简单的矩形相交检查\n return wx < x + width && wx + ww > x && wy < y + height && wy + wh > y;\n });\n\n return isVisible;\n }\n\n /**\n * 移除指定窗口的状态\n * @param windowName 窗口名称\n */\n public removeState(windowName: string): void {\n // 检查是否存在该窗口的状态\n if (this.state[windowName]) {\n // 从内存中删除窗口状态\n delete this.state[windowName];\n // 触发持久化操作\n this.persist();\n }\n }\n\n /**\n * Dispose StateKeeper instance\n * 释放 StateKeeper 实例\n */\n public dispose(): void {\n if (this.saveTimer) {\n clearTimeout(this.saveTimer);\n this.saveTimer = null;\n }\n // Only remove from activeFiles if this instance acquired the lock\n if (this.hasLock) {\n StateKeeper.activeFiles.delete(this.stateFile);\n }\n }\n}\n"],"names":["Logger","app","path","fs","crypto","screen"],"mappings":"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"}