@talex-touch/utils 1.0.17 → 1.0.20
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.
- package/animation/window.ts +191 -0
- package/channel/index.ts +49 -1
- package/common/index.ts +2 -0
- package/common/search/gather.ts +45 -0
- package/common/search/index.ts +67 -0
- package/common/storage/constants.ts +16 -2
- package/common/storage/entity/index.ts +2 -1
- package/common/storage/entity/openers.ts +32 -0
- package/common/storage/entity/shortcut-settings.ts +22 -0
- package/common/storage/shortcut-storage.ts +58 -0
- package/common/utils/file.ts +62 -0
- package/common/{utils.ts → utils/index.ts} +14 -2
- package/common/utils/polling.ts +184 -0
- package/common/utils/task-queue.ts +108 -0
- package/common/utils/time.ts +374 -0
- package/core-box/README.md +8 -8
- package/core-box/builder/index.ts +6 -0
- package/core-box/builder/tuff-builder.example.ts.bak +258 -0
- package/core-box/builder/tuff-builder.ts +1162 -0
- package/core-box/index.ts +5 -2
- package/core-box/run-tests.sh +7 -0
- package/core-box/search.ts +1 -536
- package/core-box/tuff/index.ts +6 -0
- package/core-box/tuff/tuff-dsl.ts +1412 -0
- package/electron/clipboard-helper.ts +199 -0
- package/electron/env-tool.ts +36 -2
- package/electron/file-parsers/index.ts +8 -0
- package/electron/file-parsers/parsers/text-parser.ts +109 -0
- package/electron/file-parsers/registry.ts +92 -0
- package/electron/file-parsers/types.ts +58 -0
- package/electron/index.ts +3 -0
- package/eventbus/index.ts +0 -7
- package/index.ts +3 -1
- package/package.json +4 -28
- package/plugin/channel.ts +48 -16
- package/plugin/index.ts +194 -30
- package/plugin/log/types.ts +11 -0
- package/plugin/node/index.ts +4 -0
- package/plugin/node/logger-manager.ts +113 -0
- package/plugin/{log → node}/logger.ts +41 -7
- package/plugin/plugin-source.ts +74 -0
- package/plugin/preload.ts +5 -15
- package/plugin/providers/index.ts +2 -0
- package/plugin/providers/registry.ts +47 -0
- package/plugin/providers/types.ts +54 -0
- package/plugin/risk/index.ts +1 -0
- package/plugin/risk/types.ts +20 -0
- package/plugin/sdk/enum/bridge-event.ts +4 -0
- package/plugin/sdk/enum/index.ts +1 -0
- package/plugin/sdk/hooks/bridge.ts +68 -0
- package/plugin/sdk/hooks/index.ts +2 -1
- package/plugin/sdk/hooks/life-cycle.ts +2 -4
- package/plugin/sdk/index.ts +2 -0
- package/plugin/sdk/storage.ts +84 -0
- package/plugin/sdk/types.ts +2 -2
- package/plugin/sdk/window/index.ts +5 -3
- package/preload/index.ts +2 -0
- package/preload/loading.ts +15 -0
- package/preload/renderer.ts +41 -0
- package/renderer/hooks/arg-mapper.ts +79 -0
- package/renderer/hooks/index.ts +2 -0
- package/renderer/hooks/initialize.ts +198 -0
- package/renderer/index.ts +3 -0
- package/renderer/storage/app-settings.ts +2 -0
- package/renderer/storage/base-storage.ts +1 -0
- package/renderer/storage/openers.ts +11 -0
- package/renderer/touch-sdk/env.ts +106 -0
- package/renderer/touch-sdk/index.ts +108 -0
- package/renderer/touch-sdk/terminal.ts +85 -0
- package/renderer/touch-sdk/utils.ts +61 -0
- package/search/levenshtein-utils.ts +39 -0
- package/search/types.ts +16 -16
- package/types/index.ts +2 -1
- package/types/modules/base.ts +146 -0
- package/types/modules/index.ts +4 -0
- package/types/modules/module-lifecycle.ts +148 -0
- package/types/modules/module-manager.ts +99 -0
- package/types/modules/module.ts +112 -0
- package/types/touch-app-core.ts +16 -93
- package/core-box/types.ts +0 -384
- package/electron/window.ts +0 -71
- package/plugin/log/logger-manager.ts +0 -60
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import gsap from 'gsap'
|
|
2
|
+
import { TalexTouch } from '../types'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Window animation controller return type
|
|
6
|
+
*/
|
|
7
|
+
export interface WindowAnimationController {
|
|
8
|
+
/**
|
|
9
|
+
* Update window height with animation
|
|
10
|
+
* @param newHeight - The new height to animate to
|
|
11
|
+
* @param duration - Animation duration in seconds (default: 0.5)
|
|
12
|
+
* @returns Promise that resolves to true if animation completed successfully, false if interrupted
|
|
13
|
+
*/
|
|
14
|
+
updateHeight: (newHeight: number, duration?: number) => Promise<boolean>
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Cancel current animation
|
|
18
|
+
* @returns Promise that resolves to true if there was an animation to cancel, false otherwise
|
|
19
|
+
*/
|
|
20
|
+
cancel: () => Promise<boolean>
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Toggle window visibility
|
|
24
|
+
* @param visible - Optional parameter to explicitly set visibility state
|
|
25
|
+
* @returns Promise that resolves to true if operation completed successfully, false otherwise
|
|
26
|
+
*/
|
|
27
|
+
toggleWindow: (visible?: boolean) => Promise<boolean>
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Change current window instance
|
|
31
|
+
* @param newWindow - New TouchWindow instance
|
|
32
|
+
*/
|
|
33
|
+
changeWindow: (newWindow: TalexTouch.ITouchWindow) => void
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Tracks the state of an animation
|
|
38
|
+
*/
|
|
39
|
+
interface AnimationState {
|
|
40
|
+
tween: gsap.core.Tween | null
|
|
41
|
+
completed: boolean
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Use GSAP to animate window with cubic-bezier easing
|
|
46
|
+
* @param window - TouchWindow instance (optional, can be set later with changeWindow)
|
|
47
|
+
* @returns WindowAnimationController with updateHeight, cancel, toggleWindow, and changeWindow methods
|
|
48
|
+
*/
|
|
49
|
+
export function useWindowAnimation(window?: TalexTouch.ITouchWindow): WindowAnimationController {
|
|
50
|
+
// Store current window reference inside the function scope
|
|
51
|
+
let currentWindow: TalexTouch.ITouchWindow | null = window || null
|
|
52
|
+
|
|
53
|
+
const animationState: AnimationState = {
|
|
54
|
+
tween: null,
|
|
55
|
+
completed: false
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Check if current window is valid
|
|
60
|
+
* @returns true if window is valid, false otherwise
|
|
61
|
+
*/
|
|
62
|
+
const isWindowValid = (): boolean => {
|
|
63
|
+
return (
|
|
64
|
+
currentWindow !== null &&
|
|
65
|
+
currentWindow.window !== null &&
|
|
66
|
+
!currentWindow.window.isDestroyed()
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Get current window with validation
|
|
72
|
+
* @returns current window or throws error if invalid
|
|
73
|
+
*/
|
|
74
|
+
const getCurrentWindow = (): TalexTouch.ITouchWindow => {
|
|
75
|
+
if (!isWindowValid()) {
|
|
76
|
+
throw new Error('Window is not valid or has been destroyed')
|
|
77
|
+
}
|
|
78
|
+
return currentWindow!
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const updateHeight = async (newHeight: number, duration: number = 0.5): Promise<boolean> => {
|
|
82
|
+
try {
|
|
83
|
+
const window = getCurrentWindow()
|
|
84
|
+
|
|
85
|
+
// Cancel any existing animation
|
|
86
|
+
if (animationState.tween) {
|
|
87
|
+
animationState.tween.kill()
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Reset state for new animation
|
|
91
|
+
animationState.completed = false
|
|
92
|
+
|
|
93
|
+
const browserWindow = window.window
|
|
94
|
+
const [currentWidth, currentHeight] = browserWindow.getSize()
|
|
95
|
+
const [x, y] = browserWindow.getPosition()
|
|
96
|
+
|
|
97
|
+
return new Promise<boolean>((resolve) => {
|
|
98
|
+
animationState.tween = gsap.to(
|
|
99
|
+
{
|
|
100
|
+
height: currentHeight
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
height: newHeight,
|
|
104
|
+
duration,
|
|
105
|
+
ease: 'cubic-bezier(0.785, 0.135, 0.15, 0.86)',
|
|
106
|
+
onUpdate: function () {
|
|
107
|
+
// Check if animation was cancelled or window destroyed
|
|
108
|
+
if (!animationState.tween || !isWindowValid()) {
|
|
109
|
+
resolve(false)
|
|
110
|
+
return
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const animatedHeight = Math.round(this.targets()[0].height)
|
|
114
|
+
browserWindow.setSize(currentWidth, animatedHeight)
|
|
115
|
+
browserWindow.setPosition(x, y)
|
|
116
|
+
},
|
|
117
|
+
onComplete: () => {
|
|
118
|
+
animationState.tween = null
|
|
119
|
+
animationState.completed = true
|
|
120
|
+
resolve(true)
|
|
121
|
+
},
|
|
122
|
+
onKill: () => {
|
|
123
|
+
animationState.tween = null
|
|
124
|
+
resolve(false)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
)
|
|
128
|
+
})
|
|
129
|
+
} catch (error) {
|
|
130
|
+
console.error('Error in updateHeight:', error)
|
|
131
|
+
return Promise.resolve(false)
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const cancel = async (): Promise<boolean> => {
|
|
136
|
+
if (animationState.tween) {
|
|
137
|
+
animationState.tween.kill()
|
|
138
|
+
animationState.tween = null
|
|
139
|
+
return Promise.resolve(true)
|
|
140
|
+
}
|
|
141
|
+
return Promise.resolve(false)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const toggleWindow = async (visible?: boolean): Promise<boolean> => {
|
|
145
|
+
try {
|
|
146
|
+
const window = getCurrentWindow()
|
|
147
|
+
const browserWindow = window.window
|
|
148
|
+
|
|
149
|
+
// Determine target visibility state
|
|
150
|
+
const targetVisible = visible !== undefined ? visible : !browserWindow.isVisible()
|
|
151
|
+
|
|
152
|
+
if (targetVisible) {
|
|
153
|
+
// Show window
|
|
154
|
+
browserWindow.show()
|
|
155
|
+
} else {
|
|
156
|
+
// Hide window
|
|
157
|
+
if (process.platform === 'darwin') {
|
|
158
|
+
// On macOS, we can simply hide the window
|
|
159
|
+
browserWindow.hide()
|
|
160
|
+
} else {
|
|
161
|
+
// On other platforms, move window far off-screen before hiding
|
|
162
|
+
browserWindow.setPosition(-100000, -100000)
|
|
163
|
+
browserWindow.hide()
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return Promise.resolve(true)
|
|
168
|
+
} catch (error) {
|
|
169
|
+
console.error('Error in toggleWindow:', error)
|
|
170
|
+
return Promise.resolve(false)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const changeWindow = (newWindow: TalexTouch.ITouchWindow): void => {
|
|
175
|
+
// Cancel any ongoing animation
|
|
176
|
+
if (animationState.tween) {
|
|
177
|
+
animationState.tween.kill()
|
|
178
|
+
animationState.tween = null
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Set new window
|
|
182
|
+
currentWindow = newWindow
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
updateHeight,
|
|
187
|
+
cancel,
|
|
188
|
+
toggleWindow,
|
|
189
|
+
changeWindow
|
|
190
|
+
}
|
|
191
|
+
}
|
package/channel/index.ts
CHANGED
|
@@ -21,6 +21,7 @@ export interface ITouchChannel {
|
|
|
21
21
|
regChannel(type: ChannelType, eventName: string, callback: (data: StandardChannelData) => any): () => void
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
|
+
* @deprecated Use sendMain instead
|
|
24
25
|
* Send a message to a channel
|
|
25
26
|
* @param type {@link ChannelType} The type of channel
|
|
26
27
|
* @param eventName {string} The name of event, must be unique in the channel {@link ChannelType}
|
|
@@ -29,6 +30,7 @@ export interface ITouchChannel {
|
|
|
29
30
|
send(type: ChannelType, eventName: string, arg?: any): Promise<any>
|
|
30
31
|
|
|
31
32
|
/**
|
|
33
|
+
* @deprecated Use sendToMain instead
|
|
32
34
|
* Send a message to a channel with settled window
|
|
33
35
|
* @param win {@link Electron.BrowserWindow} the window you want to sent
|
|
34
36
|
* @param type {@link ChannelType} The type of channel
|
|
@@ -36,6 +38,51 @@ export interface ITouchChannel {
|
|
|
36
38
|
* @param arg {any} The arguments of the message
|
|
37
39
|
*/
|
|
38
40
|
sendTo(win: Electron.BrowserWindow, type: ChannelType, eventName: string, arg: any): Promise<any>
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Send a message to main process
|
|
44
|
+
* @param eventName {string} The name of event, must be unique in the channel {@link ChannelType}
|
|
45
|
+
* @param arg {any} The arguments of the message
|
|
46
|
+
*/
|
|
47
|
+
sendMain(eventName: string, arg?: any): Promise<any>
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Send a message to main process with settled window
|
|
51
|
+
* @param win {@link Electron.BrowserWindow} the window you want to sent
|
|
52
|
+
* @param eventName {string} The name of event, must be unique in the channel {@link ChannelType}
|
|
53
|
+
* @param arg {any} The arguments of the message
|
|
54
|
+
*/
|
|
55
|
+
sendToMain(win: Electron.BrowserWindow, eventName: string, arg?: any): Promise<any>
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Send a message to all plugin process with settled window
|
|
59
|
+
* @param eventName {string} The name of event, must be unique in the channel {@link ChannelType}
|
|
60
|
+
* @param arg {any} The arguments of the message
|
|
61
|
+
*/
|
|
62
|
+
sendPlugin(pluginName: string, eventName: string, arg?: any): Promise<any>
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Send a message to plugin process with settled window
|
|
66
|
+
* @param pluginName {string} The name of plugin
|
|
67
|
+
* @param eventName {string} The name of event, must be unique in the channel {@link ChannelType}
|
|
68
|
+
* @param arg {any} The arguments of the message
|
|
69
|
+
*/
|
|
70
|
+
sendToPlugin(pluginName: string, eventName: string, arg?: any): Promise<any>
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Request a encrypted name key. This key cannot decrypted to get the original name.
|
|
74
|
+
* After use, you should revoke this key.
|
|
75
|
+
* @description Request a encrypted name key, and return the encrypted key
|
|
76
|
+
* @param name {string} The name of key
|
|
77
|
+
*/
|
|
78
|
+
requestKey: (name: string) => string
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Unregister a encrypted name key
|
|
82
|
+
* @description Unregister a encrypted name key, and return the encrypted key
|
|
83
|
+
* @param key {string} The encrypted key
|
|
84
|
+
*/
|
|
85
|
+
revokeKey: (key: string) => boolean
|
|
39
86
|
}
|
|
40
87
|
|
|
41
88
|
export interface ITouchClientChannel {
|
|
@@ -84,6 +131,7 @@ export interface RawChannelHeaderData {
|
|
|
84
131
|
status: "reply" | "request";
|
|
85
132
|
type: ChannelType;
|
|
86
133
|
_originData?: any;
|
|
134
|
+
uniqueKey?: string
|
|
87
135
|
event?: Electron.IpcMainEvent | Electron.IpcRendererEvent;
|
|
88
136
|
}
|
|
89
137
|
|
|
@@ -105,4 +153,4 @@ export interface StandardChannelData extends RawStandardChannelData {
|
|
|
105
153
|
|
|
106
154
|
export type IChannelData = any //boolean | number | string | null | undefined | {
|
|
107
155
|
// [prop: string]: any
|
|
108
|
-
// }
|
|
156
|
+
// }
|
package/common/index.ts
CHANGED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { TuffUpdate } from "."
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @interface ITuffGatherOptions
|
|
5
|
+
* @description Configuration options for the search result aggregator.
|
|
6
|
+
*/
|
|
7
|
+
export interface ITuffGatherOptions {
|
|
8
|
+
/**
|
|
9
|
+
* The number of providers to run in parallel.
|
|
10
|
+
* @default 3
|
|
11
|
+
*/
|
|
12
|
+
concurrency?: number
|
|
13
|
+
/**
|
|
14
|
+
* The time to wait for more results before flushing the buffer.
|
|
15
|
+
* @default 100
|
|
16
|
+
*/
|
|
17
|
+
coalesceGapMs?: number
|
|
18
|
+
/**
|
|
19
|
+
* A shorter grace period for the first batch to ensure a quick initial response.
|
|
20
|
+
* @default 50
|
|
21
|
+
*/
|
|
22
|
+
firstBatchGraceMs?: number
|
|
23
|
+
/**
|
|
24
|
+
* A small debounce delay for the push function to avoid rapid-fire updates.
|
|
25
|
+
* @default 10
|
|
26
|
+
*/
|
|
27
|
+
debouncePushMs?: number
|
|
28
|
+
/**
|
|
29
|
+
* The maximum time to wait for a single provider to return results.
|
|
30
|
+
* @default 5000
|
|
31
|
+
*/
|
|
32
|
+
taskTimeoutMs?: number
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Defines the type signature for the real-time update callback function.
|
|
37
|
+
* @param update - The data object containing update information.
|
|
38
|
+
*/
|
|
39
|
+
export type TuffAggregatorCallback = (update: TuffUpdate) => void
|
|
40
|
+
|
|
41
|
+
export interface IGatherController {
|
|
42
|
+
abort: () => void
|
|
43
|
+
promise: Promise<number>
|
|
44
|
+
signal: AbortSignal
|
|
45
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { ISearchProvider, TuffQuery, TuffSearchResult } from '@talex-touch/utils'
|
|
2
|
+
|
|
3
|
+
export * from './gather'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Represents a single update pushed from the search-gatherer.
|
|
7
|
+
* It provides a snapshot of the search progress at a point in time.
|
|
8
|
+
*/
|
|
9
|
+
export interface TuffUpdate {
|
|
10
|
+
/**
|
|
11
|
+
* New search results from the current push batch.
|
|
12
|
+
* Each element is a complete TuffSearchResult from a provider.
|
|
13
|
+
*/
|
|
14
|
+
newResults: TuffSearchResult[]
|
|
15
|
+
/**
|
|
16
|
+
* Total number of items aggregated so far.
|
|
17
|
+
*/
|
|
18
|
+
totalCount: number
|
|
19
|
+
/**
|
|
20
|
+
* Flag indicating whether all search tasks (both default and fallback queues) have completed.
|
|
21
|
+
*/
|
|
22
|
+
isDone: boolean
|
|
23
|
+
/**
|
|
24
|
+
* Flag indicating whether the search was cancelled.
|
|
25
|
+
*/
|
|
26
|
+
cancelled?: boolean
|
|
27
|
+
/**
|
|
28
|
+
* Statistics about the performance of each search provider.
|
|
29
|
+
*/
|
|
30
|
+
sourceStats?: TuffSearchResult['sources']
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Search Engine Interface (formerly ISearchEngine)
|
|
36
|
+
*
|
|
37
|
+
* Defines the core functionality of the search aggregator and orchestrator.
|
|
38
|
+
*/
|
|
39
|
+
export interface ISearchEngine<C> {
|
|
40
|
+
/**
|
|
41
|
+
* Registers a search provider with the engine.
|
|
42
|
+
* @param provider - An instance of ISearchProvider.
|
|
43
|
+
*/
|
|
44
|
+
registerProvider(provider: ISearchProvider<C>): void
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Unregisters a search provider by its unique ID.
|
|
48
|
+
* @param providerId - The unique ID of the provider to remove.
|
|
49
|
+
*/
|
|
50
|
+
unregisterProvider(providerId: string): void
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Executes a search across all registered and relevant providers.
|
|
54
|
+
* It aggregates, scores, and ranks the results.
|
|
55
|
+
*
|
|
56
|
+
* @param query - The search query object.
|
|
57
|
+
* @returns A promise that resolves to a TuffSearchResult object,
|
|
58
|
+
* containing the ranked items and metadata about the search operation.
|
|
59
|
+
*/
|
|
60
|
+
search(query: TuffQuery): Promise<TuffSearchResult>
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Performs background maintenance tasks, such as pre-heating caches,
|
|
64
|
+
* refreshing indexes, etc.
|
|
65
|
+
*/
|
|
66
|
+
maintain(): void
|
|
67
|
+
}
|
|
@@ -1,3 +1,17 @@
|
|
|
1
1
|
export enum StorageList {
|
|
2
|
-
APP_SETTING =
|
|
3
|
-
|
|
2
|
+
APP_SETTING = 'app-setting.ini',
|
|
3
|
+
SHORTCUT_SETTING = 'shortcut-setting.ini',
|
|
4
|
+
OPENERS = 'openers.json'
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Defines keys for the global configuration stored in the database `config` table.
|
|
9
|
+
* Using an enum prevents magic strings and ensures consistency across the application.
|
|
10
|
+
*/
|
|
11
|
+
export enum ConfigKeys {
|
|
12
|
+
/**
|
|
13
|
+
* Stores the timestamp of the last successful full application scan using mdfind.
|
|
14
|
+
* This is used to schedule the next comprehensive scan.
|
|
15
|
+
*/
|
|
16
|
+
APP_PROVIDER_LAST_MDFIND_SCAN = 'app_provider_last_mdfind_scan'
|
|
17
|
+
}
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export * from './app-settings'
|
|
1
|
+
export * from './app-settings'
|
|
2
|
+
export * from './openers'
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export interface OpenerInfo {
|
|
2
|
+
/**
|
|
3
|
+
* Bundle identifier of the application responsible for handling the file type.
|
|
4
|
+
*/
|
|
5
|
+
bundleId: string
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Display name of the application.
|
|
9
|
+
*/
|
|
10
|
+
name: string
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Icon for the application (data URL or resolvable path).
|
|
14
|
+
*/
|
|
15
|
+
logo: string
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Absolute path of the application bundle/executable.
|
|
19
|
+
*/
|
|
20
|
+
path?: string
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* ISO timestamp representing when this mapping was last refreshed.
|
|
24
|
+
*/
|
|
25
|
+
lastResolvedAt?: string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type OpenersMap = Record<string, OpenerInfo>
|
|
29
|
+
|
|
30
|
+
const _openersOriginData: OpenersMap = {}
|
|
31
|
+
|
|
32
|
+
export const openersOriginData = Object.freeze(_openersOriginData)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export enum ShortcutType {
|
|
2
|
+
MAIN = 'main',
|
|
3
|
+
RENDERER = 'renderer',
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface ShortcutMeta {
|
|
7
|
+
creationTime: number;
|
|
8
|
+
modificationTime: number;
|
|
9
|
+
author: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface Shortcut {
|
|
14
|
+
id: string;
|
|
15
|
+
accelerator: string;
|
|
16
|
+
type: ShortcutType;
|
|
17
|
+
meta: ShortcutMeta;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type ShortcutSetting = Shortcut[];
|
|
21
|
+
|
|
22
|
+
export const shortcutSettingOriginData: ShortcutSetting = [];
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { StorageList } from './constants'
|
|
2
|
+
import { shortcutSettingOriginData, ShortcutSetting, Shortcut } from './entity/shortcut-settings'
|
|
3
|
+
|
|
4
|
+
class ShortcutStorage {
|
|
5
|
+
private _config: ShortcutSetting = []
|
|
6
|
+
|
|
7
|
+
constructor(private readonly storage: {
|
|
8
|
+
getConfig: (name: string) => any,
|
|
9
|
+
saveConfig: (name: string, content?: string) => void,
|
|
10
|
+
}) {
|
|
11
|
+
this.init()
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
private init() {
|
|
15
|
+
const config = this.storage.getConfig(StorageList.SHORTCUT_SETTING)
|
|
16
|
+
if (!config || !Array.isArray(config) || config.length === 0) {
|
|
17
|
+
this._config = [...shortcutSettingOriginData]
|
|
18
|
+
this._save()
|
|
19
|
+
} else {
|
|
20
|
+
this._config = config
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
private _save() {
|
|
25
|
+
this.storage.saveConfig(StorageList.SHORTCUT_SETTING, JSON.stringify(this._config, null, 2))
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
getAllShortcuts(): Shortcut[] {
|
|
29
|
+
return this._config
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
getShortcutById(id: string): Shortcut | undefined {
|
|
33
|
+
return this._config.find(s => s.id === id)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
addShortcut(shortcut: Shortcut): boolean {
|
|
37
|
+
if (this.getShortcutById(shortcut.id)) {
|
|
38
|
+
console.warn(`Shortcut with ID ${shortcut.id} already exists.`)
|
|
39
|
+
return false
|
|
40
|
+
}
|
|
41
|
+
this._config.push(shortcut)
|
|
42
|
+
this._save()
|
|
43
|
+
return true
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
updateShortcutAccelerator(id: string, newAccelerator: string): boolean {
|
|
47
|
+
const shortcut = this.getShortcutById(id)
|
|
48
|
+
if (!shortcut) {
|
|
49
|
+
return false
|
|
50
|
+
}
|
|
51
|
+
shortcut.accelerator = newAccelerator
|
|
52
|
+
shortcut.meta.modificationTime = Date.now()
|
|
53
|
+
this._save()
|
|
54
|
+
return true
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export default ShortcutStorage
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import path from 'path-browserify'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Enum for various file types.
|
|
5
|
+
* @enum {string}
|
|
6
|
+
*/
|
|
7
|
+
export enum FileType {
|
|
8
|
+
Image = 'Image',
|
|
9
|
+
Document = 'Document',
|
|
10
|
+
Audio = 'Audio',
|
|
11
|
+
Video = 'Video',
|
|
12
|
+
Archive = 'Archive',
|
|
13
|
+
Code = 'Code',
|
|
14
|
+
Text = 'Text',
|
|
15
|
+
Design = 'Design',
|
|
16
|
+
Model3D = '3D Model',
|
|
17
|
+
Font = 'Font',
|
|
18
|
+
Spreadsheet = 'Spreadsheet',
|
|
19
|
+
Presentation = 'Presentation',
|
|
20
|
+
Ebook = 'Ebook',
|
|
21
|
+
Other = 'Other'
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const extensionMap: Map<FileType, Set<string>> = new Map([
|
|
25
|
+
[FileType.Image, new Set(['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp', '.svg', '.ico', '.tiff'])],
|
|
26
|
+
[FileType.Document, new Set(['.doc', '.docx', '.pdf', '.odt', '.rtf'])],
|
|
27
|
+
[FileType.Audio, new Set(['.mp3', '.wav', '.ogg', '.flac', '.aac', '.m4a'])],
|
|
28
|
+
[FileType.Video, new Set(['.mp4', '.avi', '.mov', '.wmv', '.mkv', '.flv', '.webm'])],
|
|
29
|
+
[FileType.Archive, new Set(['.zip', '.rar', '.7z', '.tar', '.gz', '.bz2'])],
|
|
30
|
+
[FileType.Code, new Set(['.js', '.ts', '.jsx', '.tsx', '.html', '.css', '.scss', '.json', '.xml', '.java', '.py', '.c', '.cpp', '.go', '.rs', '.php', '.sh'])],
|
|
31
|
+
[FileType.Text, new Set(['.txt', '.md', '.log'])],
|
|
32
|
+
[FileType.Design, new Set(['.psd', '.ai', '.fig', '.sketch', '.xd', '.afdesign'])],
|
|
33
|
+
[FileType.Model3D, new Set(['.obj', '.fbx', '.stl', '.dae', '.blend', '.3ds'])],
|
|
34
|
+
[FileType.Font, new Set(['.ttf', '.otf', '.woff', '.woff2'])],
|
|
35
|
+
[FileType.Spreadsheet, new Set(['.xls', '.xlsx', '.csv', '.numbers'])],
|
|
36
|
+
[FileType.Presentation, new Set(['.ppt', '.pptx', '.key'])],
|
|
37
|
+
[FileType.Ebook, new Set(['.epub', '.mobi', '.azw'])]
|
|
38
|
+
]);
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Get the file type from a file path.
|
|
42
|
+
* @param {string} filePath - The path to the file.
|
|
43
|
+
* @returns {FileType} The type of the file.
|
|
44
|
+
*/
|
|
45
|
+
export function getFileTypeFromPath(filePath: string): FileType {
|
|
46
|
+
const extension = path.extname(filePath).toLowerCase()
|
|
47
|
+
return getFileTypeFromExtension(extension)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Get the file type from a file extension.
|
|
52
|
+
* @param {string} extension - The file extension, including the dot.
|
|
53
|
+
* @returns {FileType} The type of the file.
|
|
54
|
+
*/
|
|
55
|
+
export function getFileTypeFromExtension(extension: string): FileType {
|
|
56
|
+
for (const [type, extensions] of extensionMap.entries()) {
|
|
57
|
+
if (extensions.has(extension)) {
|
|
58
|
+
return type
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return FileType.Other
|
|
62
|
+
}
|
|
@@ -101,7 +101,7 @@ export function structuredStrictStringify(value: unknown): string {
|
|
|
101
101
|
if (typeof Document !== 'undefined') {
|
|
102
102
|
if (val instanceof Node) return 'DOMNode';
|
|
103
103
|
}
|
|
104
|
-
if (val instanceof Error) return 'Error';
|
|
104
|
+
// if (val instanceof Error) return 'Error';
|
|
105
105
|
if (val instanceof WeakMap) return 'WeakMap';
|
|
106
106
|
if (val instanceof WeakSet) return 'WeakSet';
|
|
107
107
|
if (typeof val === 'object' && val !== null && val.constructor?.name === 'Proxy') return 'Proxy';
|
|
@@ -112,7 +112,7 @@ export function structuredStrictStringify(value: unknown): string {
|
|
|
112
112
|
function serialize(val: any, path: string): any {
|
|
113
113
|
const type = getType(val);
|
|
114
114
|
// Block disallowed/unsafe types and edge cases for structured-clone
|
|
115
|
-
if (badTypes.includes(typeof val) || type === 'DOMNode' || type === '
|
|
115
|
+
if (badTypes.includes(typeof val) || type === 'DOMNode' || type === 'Proxy' || type === 'WeakMap' || type === 'WeakSet' || type === 'BigInt') {
|
|
116
116
|
throw new Error(`Cannot serialize property at path "${path}": type "${type}"`);
|
|
117
117
|
}
|
|
118
118
|
// JSON doesn't support undefined, skip it for values in objects, preserve in arrays as null
|
|
@@ -130,6 +130,13 @@ export function structuredStrictStringify(value: unknown): string {
|
|
|
130
130
|
return `[Circular ~${seen.get(val)}]`; // You could just throw if you dislike this fallback!
|
|
131
131
|
}
|
|
132
132
|
seen.set(val, path);
|
|
133
|
+
if (val instanceof Error) {
|
|
134
|
+
return {
|
|
135
|
+
name: val.name,
|
|
136
|
+
message: val.message,
|
|
137
|
+
stack: val.stack,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
133
140
|
if (Array.isArray(val)) {
|
|
134
141
|
return val.map((item, idx) => serialize(item, `${path}[${idx}]`));
|
|
135
142
|
}
|
|
@@ -158,3 +165,8 @@ export function structuredStrictStringify(value: unknown): string {
|
|
|
158
165
|
|
|
159
166
|
return JSON.stringify(serialize(value, 'root'));
|
|
160
167
|
}
|
|
168
|
+
|
|
169
|
+
export { runAdaptiveTaskQueue, type AdaptiveTaskQueueOptions } from './task-queue'
|
|
170
|
+
|
|
171
|
+
export * from './time'
|
|
172
|
+
export * from './file'
|