@talex-touch/utils 1.0.42 → 1.0.45
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/.eslintcache +1 -0
- package/__tests__/cloud-sync-sdk.test.ts +442 -0
- package/__tests__/icons/icons.test.ts +84 -0
- package/__tests__/plugin-sdk-lifecycle.test.ts +130 -0
- package/__tests__/power-sdk.test.ts +143 -0
- package/__tests__/preset-export-types.test.ts +108 -0
- package/__tests__/search/fuzzy-match.test.ts +137 -0
- package/__tests__/transport/port-policy.test.ts +44 -0
- package/__tests__/transport-domain-sdks.test.ts +152 -0
- package/__tests__/types/update.test.ts +67 -0
- package/account/account-sdk.ts +915 -0
- package/account/index.ts +2 -0
- package/account/types.ts +321 -0
- package/analytics/client.ts +136 -0
- package/analytics/index.ts +2 -0
- package/analytics/types.ts +156 -0
- package/animation/auto-resize.ts +322 -0
- package/animation/window-node.ts +26 -19
- package/auth/clerk-types.ts +12 -30
- package/auth/index.ts +0 -2
- package/auth/useAuthState.ts +6 -14
- package/base/index.ts +2 -0
- package/base/log-level.ts +105 -0
- package/channel/index.ts +170 -69
- package/cloud-sync/cloud-sync-sdk.ts +450 -0
- package/cloud-sync/index.ts +1 -0
- package/common/file-scan-utils.ts +17 -9
- package/common/index.ts +4 -0
- package/common/logger/index.ts +46 -0
- package/common/logger/logger-manager.ts +303 -0
- package/common/logger/module-logger.ts +270 -0
- package/common/logger/transport-logger.ts +234 -0
- package/common/logger/types.ts +93 -0
- package/common/search/gather.ts +48 -6
- package/common/search/index.ts +8 -0
- package/common/storage/constants.ts +13 -0
- package/common/storage/entity/app-settings.ts +245 -0
- package/common/storage/entity/index.ts +3 -0
- package/common/storage/entity/layout-atom-types.ts +147 -0
- package/common/storage/entity/openers.ts +1 -0
- package/common/storage/entity/preset-cloud-api.ts +132 -0
- package/common/storage/entity/preset-export-types.ts +256 -0
- package/common/storage/entity/shortcut-settings.ts +1 -0
- package/common/storage/shortcut-storage.ts +11 -0
- package/common/utils/clone-diagnostics.ts +105 -0
- package/common/utils/file.ts +16 -8
- package/common/utils/index.ts +6 -2
- package/common/utils/payload-preview.ts +173 -0
- package/common/utils/polling.ts +167 -13
- package/common/utils/safe-path.ts +103 -0
- package/common/utils/safe-shell.ts +115 -0
- package/common/utils/task-queue.ts +4 -1
- package/core-box/builder/tuff-builder.ts +0 -1
- package/core-box/index.ts +1 -1
- package/core-box/recommendation.ts +38 -1
- package/core-box/tuff/tuff-dsl.ts +32 -0
- package/electron/download-manager.ts +10 -7
- package/electron/env-tool.ts +42 -40
- package/electron/index.ts +0 -1
- package/env/index.ts +156 -0
- package/eslint.config.js +55 -0
- package/i18n/index.ts +62 -0
- package/i18n/locales/en.json +226 -0
- package/i18n/locales/zh.json +226 -0
- package/i18n/message-keys.ts +236 -0
- package/i18n/resolver.ts +181 -0
- package/icons/index.ts +257 -0
- package/icons/svg.ts +69 -0
- package/index.ts +9 -1
- package/intelligence/client.ts +72 -42
- package/market/constants.ts +9 -5
- package/market/index.ts +1 -1
- package/market/types.ts +19 -4
- package/package.json +15 -5
- package/permission/index.ts +143 -46
- package/permission/legacy.ts +26 -0
- package/permission/registry.ts +304 -0
- package/permission/types.ts +164 -0
- package/plugin/channel.ts +68 -39
- package/plugin/index.ts +80 -7
- package/plugin/install.ts +3 -0
- package/plugin/log/types.ts +22 -5
- package/plugin/node/logger-manager.ts +11 -3
- package/plugin/node/logger.ts +24 -17
- package/plugin/preload.ts +25 -2
- package/plugin/providers/index.ts +4 -4
- package/plugin/providers/market-client.ts +6 -3
- package/plugin/providers/npm-provider.ts +22 -7
- package/plugin/providers/tpex-provider.ts +22 -8
- package/plugin/sdk/box-items.ts +14 -0
- package/plugin/sdk/box-sdk.ts +64 -0
- package/plugin/sdk/channel.ts +119 -4
- package/plugin/sdk/clipboard.ts +26 -12
- package/plugin/sdk/cloud-sync.ts +113 -0
- package/plugin/sdk/common.ts +19 -11
- package/plugin/sdk/core-box.ts +6 -15
- package/plugin/sdk/division-box.ts +160 -65
- package/plugin/sdk/examples/storage-onDidChange-example.js +5 -2
- package/plugin/sdk/feature-sdk.ts +111 -76
- package/plugin/sdk/flow.ts +146 -45
- package/plugin/sdk/hooks/bridge.ts +13 -6
- package/plugin/sdk/hooks/life-cycle.ts +35 -16
- package/plugin/sdk/index.ts +14 -3
- package/plugin/sdk/intelligence.ts +87 -0
- package/plugin/sdk/meta/README.md +179 -0
- package/plugin/sdk/meta-sdk.ts +244 -0
- package/plugin/sdk/notification.ts +9 -0
- package/plugin/sdk/plugin-info.ts +64 -0
- package/plugin/sdk/power.ts +155 -0
- package/plugin/sdk/recommend.ts +21 -0
- package/plugin/sdk/service/index.ts +12 -8
- package/plugin/sdk/sqlite.ts +141 -0
- package/plugin/sdk/storage.ts +2 -6
- package/plugin/sdk/system.ts +2 -9
- package/plugin/sdk/temp-files.ts +41 -0
- package/plugin/sdk/touch-sdk.ts +18 -0
- package/plugin/sdk/types.ts +44 -4
- package/plugin/sdk/window/index.ts +12 -9
- package/plugin/sdk-version.ts +231 -0
- package/preload/renderer.ts +3 -2
- package/renderer/hooks/arg-mapper.ts +16 -2
- package/renderer/hooks/index.ts +13 -0
- package/renderer/hooks/initialize.ts +2 -1
- package/renderer/hooks/use-agent-market-sdk.ts +7 -0
- package/renderer/hooks/use-agent-market.ts +106 -0
- package/renderer/hooks/use-agents-sdk.ts +7 -0
- package/renderer/hooks/use-app-sdk.ts +7 -0
- package/renderer/hooks/use-channel.ts +33 -4
- package/renderer/hooks/use-download-sdk.ts +21 -0
- package/renderer/hooks/use-intelligence-sdk.ts +7 -0
- package/renderer/hooks/use-intelligence-stats.ts +290 -0
- package/renderer/hooks/use-intelligence.ts +55 -214
- package/renderer/hooks/use-market-sdk.ts +16 -0
- package/renderer/hooks/use-notification-sdk.ts +7 -0
- package/renderer/hooks/use-permission-sdk.ts +7 -0
- package/renderer/hooks/use-permission.ts +325 -0
- package/renderer/hooks/use-platform-sdk.ts +7 -0
- package/renderer/hooks/use-plugin-sdk.ts +16 -0
- package/renderer/hooks/use-settings-sdk.ts +7 -0
- package/renderer/hooks/use-update-sdk.ts +21 -0
- package/renderer/index.ts +1 -0
- package/renderer/ref.ts +19 -10
- package/renderer/shared/components/SharedPluginDetailContent.vue +84 -0
- package/renderer/shared/components/SharedPluginDetailHeader.vue +116 -0
- package/renderer/shared/components/SharedPluginDetailMetaList.vue +39 -0
- package/renderer/shared/components/SharedPluginDetailReadme.vue +45 -0
- package/renderer/shared/components/SharedPluginDetailVersions.vue +98 -0
- package/renderer/shared/components/index.ts +5 -0
- package/renderer/shared/components/shims-vue.d.ts +5 -0
- package/renderer/shared/index.ts +2 -0
- package/renderer/shared/plugin-detail.ts +62 -0
- package/renderer/storage/app-settings.ts +3 -1
- package/renderer/storage/base-storage.ts +508 -82
- package/renderer/storage/intelligence-storage.ts +31 -40
- package/renderer/storage/openers.ts +3 -1
- package/renderer/storage/storage-subscription.ts +126 -42
- package/renderer/touch-sdk/env.ts +10 -10
- package/renderer/touch-sdk/index.ts +114 -18
- package/renderer/touch-sdk/terminal.ts +24 -13
- package/search/feature-matcher.ts +279 -0
- package/search/fuzzy-match.ts +64 -34
- package/search/index.ts +10 -0
- package/search/levenshtein-utils.ts +17 -11
- package/transport/errors.ts +310 -0
- package/transport/event/builder.ts +378 -0
- package/transport/event/index.ts +7 -0
- package/transport/event/types.ts +292 -0
- package/transport/events/index.ts +2690 -0
- package/transport/events/meta-overlay.ts +79 -0
- package/transport/events/types/agents.ts +177 -0
- package/transport/events/types/app-index.ts +20 -0
- package/transport/events/types/app.ts +475 -0
- package/transport/events/types/box-item.ts +222 -0
- package/transport/events/types/clipboard.ts +80 -0
- package/transport/events/types/core-box.ts +534 -0
- package/transport/events/types/device-idle.ts +7 -0
- package/transport/events/types/division-box.ts +99 -0
- package/transport/events/types/download.ts +115 -0
- package/transport/events/types/file-index.ts +84 -0
- package/transport/events/types/flow.ts +149 -0
- package/transport/events/types/index.ts +70 -0
- package/transport/events/types/market.ts +39 -0
- package/transport/events/types/meta-overlay.ts +184 -0
- package/transport/events/types/notification.ts +140 -0
- package/transport/events/types/permission.ts +90 -0
- package/transport/events/types/platform.ts +8 -0
- package/transport/events/types/plugin.ts +631 -0
- package/transport/events/types/sentry.ts +20 -0
- package/transport/events/types/storage.ts +208 -0
- package/transport/events/types/transport.ts +60 -0
- package/transport/events/types/tray.ts +16 -0
- package/transport/events/types/update.ts +78 -0
- package/transport/index.ts +141 -0
- package/transport/main.ts +2 -0
- package/transport/prelude.ts +208 -0
- package/transport/sdk/constants.ts +29 -0
- package/transport/sdk/domains/agents-market.ts +47 -0
- package/transport/sdk/domains/agents.ts +62 -0
- package/transport/sdk/domains/app.ts +48 -0
- package/transport/sdk/domains/disposable.ts +35 -0
- package/transport/sdk/domains/download.ts +139 -0
- package/transport/sdk/domains/index.ts +13 -0
- package/transport/sdk/domains/intelligence.ts +616 -0
- package/transport/sdk/domains/market.ts +35 -0
- package/transport/sdk/domains/notification.ts +62 -0
- package/transport/sdk/domains/permission.ts +85 -0
- package/transport/sdk/domains/platform.ts +19 -0
- package/transport/sdk/domains/plugin.ts +144 -0
- package/transport/sdk/domains/settings.ts +102 -0
- package/transport/sdk/domains/update.ts +64 -0
- package/transport/sdk/index.ts +60 -0
- package/transport/sdk/main-transport.ts +710 -0
- package/transport/sdk/main.ts +9 -0
- package/transport/sdk/plugin-transport.ts +654 -0
- package/transport/sdk/port-policy.ts +38 -0
- package/transport/sdk/renderer-transport.ts +1165 -0
- package/transport/types.ts +605 -0
- package/types/agent.ts +399 -0
- package/types/cloud-sync.ts +157 -0
- package/types/division-box.ts +31 -31
- package/types/download.ts +1 -0
- package/types/flow.ts +63 -12
- package/types/icon.ts +2 -1
- package/types/index.ts +5 -0
- package/types/intelligence.ts +166 -173
- package/types/modules/base.ts +2 -0
- package/types/path-browserify.d.ts +5 -0
- package/types/platform.ts +12 -0
- package/types/startup-info.ts +32 -0
- package/types/touch-app-core.ts +8 -8
- package/types/update.ts +94 -1
- package/vitest.config.ts +25 -0
- package/auth/useClerkConfig.ts +0 -40
- package/auth/useClerkProvider.ts +0 -52
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { getCurrentInstance } from 'vue'
|
|
2
|
+
import { hasWindow } from '../../env'
|
|
3
|
+
|
|
4
|
+
export interface PluginRuntimeInfo {
|
|
5
|
+
name?: string
|
|
6
|
+
sdkapi?: number
|
|
7
|
+
[key: string]: unknown
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const DEFAULT_PLUGIN_INFO_ERROR
|
|
11
|
+
= '[Plugin SDK] Plugin info not available. Make sure this is called in a plugin context.'
|
|
12
|
+
|
|
13
|
+
const DEFAULT_PLUGIN_NAME_ERROR
|
|
14
|
+
= '[Plugin SDK] Cannot determine plugin name. Make sure this is called in a plugin context.'
|
|
15
|
+
|
|
16
|
+
function tryResolvePluginInfoFromInstance(): PluginRuntimeInfo | null {
|
|
17
|
+
const instance = getCurrentInstance()
|
|
18
|
+
if (!instance) {
|
|
19
|
+
return null
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const proxy = instance.proxy as { $plugin?: PluginRuntimeInfo } | null
|
|
23
|
+
if (proxy?.$plugin) {
|
|
24
|
+
return proxy.$plugin
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const appPlugin = instance.appContext?.config.globalProperties?.$plugin as PluginRuntimeInfo | undefined
|
|
28
|
+
return appPlugin ?? null
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function tryUsePluginInfo(): PluginRuntimeInfo | null {
|
|
32
|
+
const injected = tryResolvePluginInfoFromInstance()
|
|
33
|
+
if (injected) {
|
|
34
|
+
return injected
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (!hasWindow()) {
|
|
38
|
+
return null
|
|
39
|
+
}
|
|
40
|
+
return (window as any)?.$plugin ?? null
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function usePluginInfo(errorMessage = DEFAULT_PLUGIN_INFO_ERROR): PluginRuntimeInfo {
|
|
44
|
+
const plugin = tryUsePluginInfo()
|
|
45
|
+
if (!plugin) {
|
|
46
|
+
throw new Error(errorMessage)
|
|
47
|
+
}
|
|
48
|
+
return plugin
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function usePluginName(errorMessage = DEFAULT_PLUGIN_NAME_ERROR): string {
|
|
52
|
+
const plugin = usePluginInfo(errorMessage)
|
|
53
|
+
const name = typeof plugin.name === 'string' ? plugin.name : undefined
|
|
54
|
+
if (!name) {
|
|
55
|
+
throw new Error(errorMessage)
|
|
56
|
+
}
|
|
57
|
+
return name
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function tryGetPluginSdkApi(): number | undefined {
|
|
61
|
+
const plugin = tryUsePluginInfo()
|
|
62
|
+
const sdkapi = plugin?.sdkapi
|
|
63
|
+
return typeof sdkapi === 'number' ? sdkapi : undefined
|
|
64
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import type { ITouchClientChannel } from '@talex-touch/utils/channel'
|
|
2
|
+
import type { BatteryStatusPayload, FileIndexBatteryStatus } from '../../transport/events'
|
|
3
|
+
import { AppEvents } from '../../transport/events'
|
|
4
|
+
import { createPluginTuffTransport } from '../../transport/sdk/plugin-transport'
|
|
5
|
+
import { ensureRendererChannel } from './channel'
|
|
6
|
+
|
|
7
|
+
const DEFAULT_LOW_POWER_THRESHOLD = 20
|
|
8
|
+
|
|
9
|
+
function normalizeThreshold(threshold?: number): number {
|
|
10
|
+
if (typeof threshold !== 'number' || !Number.isFinite(threshold)) {
|
|
11
|
+
return DEFAULT_LOW_POWER_THRESHOLD
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const normalized = Math.floor(threshold)
|
|
15
|
+
if (normalized <= 0) return 1
|
|
16
|
+
if (normalized > 100) return 100
|
|
17
|
+
return normalized
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function normalizePercent(percent: unknown): number | null {
|
|
21
|
+
if (typeof percent !== 'number' || !Number.isFinite(percent)) {
|
|
22
|
+
return null
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (percent < 0) return 0
|
|
26
|
+
if (percent > 100) return 100
|
|
27
|
+
return percent
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function toStatusFromBattery(
|
|
31
|
+
battery: FileIndexBatteryStatus | null | undefined,
|
|
32
|
+
threshold: number
|
|
33
|
+
): LowPowerStatus {
|
|
34
|
+
const percent = normalizePercent(battery?.level)
|
|
35
|
+
const onBattery = percent !== null ? battery?.charging === false : false
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
lowPower: onBattery && percent !== null && percent <= threshold,
|
|
39
|
+
onBattery,
|
|
40
|
+
percent,
|
|
41
|
+
threshold
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function toStatusFromBroadcast(
|
|
46
|
+
payload: BatteryStatusPayload | undefined,
|
|
47
|
+
threshold: number
|
|
48
|
+
): LowPowerStatus | null {
|
|
49
|
+
if (typeof payload?.onBattery !== 'boolean') {
|
|
50
|
+
return null
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const percent = normalizePercent(payload.percent)
|
|
54
|
+
const onBattery = payload.onBattery
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
lowPower: onBattery && percent !== null && percent <= threshold,
|
|
58
|
+
onBattery,
|
|
59
|
+
percent,
|
|
60
|
+
threshold
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function toSignature(status: LowPowerStatus): string {
|
|
65
|
+
return [status.lowPower, status.onBattery, status.percent ?? 'null', status.threshold].join(':')
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface LowPowerStatus {
|
|
69
|
+
lowPower: boolean
|
|
70
|
+
onBattery: boolean
|
|
71
|
+
percent: number | null
|
|
72
|
+
threshold: number
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface PowerSDK {
|
|
76
|
+
getLowPowerStatus: (options?: { threshold?: number }) => Promise<LowPowerStatus>
|
|
77
|
+
isLowPower: (options?: { threshold?: number }) => Promise<boolean>
|
|
78
|
+
onLowPowerChanged: (
|
|
79
|
+
callback: (status: LowPowerStatus) => void,
|
|
80
|
+
options?: { threshold?: number, emitImmediately?: boolean }
|
|
81
|
+
) => () => void
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function createPowerSDK(channel: ITouchClientChannel): PowerSDK {
|
|
85
|
+
const transport = createPluginTuffTransport(channel)
|
|
86
|
+
|
|
87
|
+
const getStatus = async (options: { threshold?: number } = {}): Promise<LowPowerStatus> => {
|
|
88
|
+
const threshold = normalizeThreshold(options.threshold)
|
|
89
|
+
try {
|
|
90
|
+
const battery = await transport.send(AppEvents.fileIndex.batteryLevel)
|
|
91
|
+
return toStatusFromBattery(battery, threshold)
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return {
|
|
95
|
+
lowPower: false,
|
|
96
|
+
onBattery: false,
|
|
97
|
+
percent: null,
|
|
98
|
+
threshold
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
getLowPowerStatus: getStatus,
|
|
105
|
+
|
|
106
|
+
async isLowPower(options: { threshold?: number } = {}): Promise<boolean> {
|
|
107
|
+
const status = await getStatus(options)
|
|
108
|
+
return status.lowPower
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
onLowPowerChanged(
|
|
112
|
+
callback: (status: LowPowerStatus) => void,
|
|
113
|
+
options: { threshold?: number, emitImmediately?: boolean } = {}
|
|
114
|
+
): () => void {
|
|
115
|
+
const threshold = normalizeThreshold(options.threshold)
|
|
116
|
+
let disposed = false
|
|
117
|
+
let lastSignature = ''
|
|
118
|
+
|
|
119
|
+
const emit = async (status: LowPowerStatus, force = false) => {
|
|
120
|
+
if (disposed) return
|
|
121
|
+
const signature = toSignature(status)
|
|
122
|
+
if (!force && signature === lastSignature) return
|
|
123
|
+
lastSignature = signature
|
|
124
|
+
callback(status)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const dispose = transport.on(AppEvents.power.batteryStatus, async (payload) => {
|
|
128
|
+
const fromBroadcast = toStatusFromBroadcast(payload, threshold)
|
|
129
|
+
if (fromBroadcast) {
|
|
130
|
+
await emit(fromBroadcast)
|
|
131
|
+
return
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const fromQuery = await getStatus({ threshold })
|
|
135
|
+
await emit(fromQuery)
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
if (options.emitImmediately !== false) {
|
|
139
|
+
void getStatus({ threshold }).then((status) => emit(status, true))
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return () => {
|
|
143
|
+
disposed = true
|
|
144
|
+
dispose()
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function usePowerSDK(): PowerSDK {
|
|
151
|
+
const channel = ensureRendererChannel(
|
|
152
|
+
'[PowerSDK] Channel not available. Make sure this is called in a plugin renderer context.'
|
|
153
|
+
)
|
|
154
|
+
return createPowerSDK(channel)
|
|
155
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { RecommendProvider } from '../../core-box/recommendation'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SDK for plugins to register custom recommendation providers.
|
|
5
|
+
*
|
|
6
|
+
* Providers registered through this SDK will be called by the RecommendationEngine
|
|
7
|
+
* when generating recommendations for the CoreBox empty-query state.
|
|
8
|
+
*/
|
|
9
|
+
export interface RecommendSDK {
|
|
10
|
+
/**
|
|
11
|
+
* Register a recommendation provider.
|
|
12
|
+
* @returns A dispose function to unregister the provider.
|
|
13
|
+
*/
|
|
14
|
+
registerProvider(provider: RecommendProvider): () => void
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Unregister a recommendation provider by its ID.
|
|
18
|
+
* @returns true if the provider was found and removed.
|
|
19
|
+
*/
|
|
20
|
+
unregisterProvider(providerId: string): boolean
|
|
21
|
+
}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import type { IService } from '../../../service'
|
|
2
|
-
import {
|
|
2
|
+
import { useChannel } from '../channel'
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
type ServiceHandler = (data: any) => unknown
|
|
5
|
+
|
|
6
|
+
export async function regService(service: IService, handler: ServiceHandler): Promise<boolean> {
|
|
7
|
+
const channel = useChannel('[Plugin SDK] Service registration requires renderer channel.')
|
|
8
|
+
const res = !!(await channel.send('service:reg', { service: service.name }))
|
|
6
9
|
|
|
7
10
|
if (res)
|
|
8
11
|
onHandleService(service, handler)
|
|
@@ -10,13 +13,14 @@ export function regService(service: IService, handler: Function): boolean {
|
|
|
10
13
|
return res
|
|
11
14
|
}
|
|
12
15
|
|
|
13
|
-
export function unRegService(service: IService): boolean {
|
|
14
|
-
|
|
16
|
+
export async function unRegService(service: IService): Promise<boolean> {
|
|
17
|
+
const channel = useChannel('[Plugin SDK] Service unregistration requires renderer channel.')
|
|
18
|
+
return !!(await channel.send('service:unreg', { service: service.name }))
|
|
15
19
|
}
|
|
16
20
|
|
|
17
|
-
export function onHandleService(service: IService, handler:
|
|
18
|
-
|
|
19
|
-
|
|
21
|
+
export function onHandleService(service: IService, handler: ServiceHandler) {
|
|
22
|
+
const channel = useChannel('[Plugin SDK] Service handling requires renderer channel.')
|
|
23
|
+
channel.regChannel('service:handle', ({ data: _data }) => {
|
|
20
24
|
const { data } = _data
|
|
21
25
|
|
|
22
26
|
// console.log('service:handle', data, service)
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { ensureRendererChannel } from './channel'
|
|
2
|
+
import { usePluginName } from './plugin-info'
|
|
3
|
+
|
|
4
|
+
export interface PluginSqliteStatement {
|
|
5
|
+
sql: string
|
|
6
|
+
params?: unknown[]
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface PluginSqliteExecuteResult {
|
|
10
|
+
rowsAffected: number
|
|
11
|
+
lastInsertRowId: number | null
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface PluginSqliteQueryResult<T extends Record<string, unknown>> {
|
|
15
|
+
rows: T[]
|
|
16
|
+
columns: string[]
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface PluginSqliteTransactionResult {
|
|
20
|
+
results: PluginSqliteExecuteResult[]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function normalizeSql(sql: string): string {
|
|
24
|
+
return typeof sql === 'string' ? sql.trim() : ''
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function normalizeParams(params?: unknown[]): unknown[] {
|
|
28
|
+
return Array.isArray(params) ? params : []
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function usePluginSqlite() {
|
|
32
|
+
const pluginName = usePluginName(
|
|
33
|
+
'[Plugin SQLite SDK] Cannot determine plugin name. Make sure this is called in a plugin context.'
|
|
34
|
+
)
|
|
35
|
+
const channel = ensureRendererChannel(
|
|
36
|
+
'[Plugin SQLite SDK] Channel not available. Make sure this is called in a plugin context.'
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
execute: async (
|
|
41
|
+
sql: string,
|
|
42
|
+
params?: unknown[]
|
|
43
|
+
): Promise<PluginSqliteExecuteResult> => {
|
|
44
|
+
const normalizedSql = normalizeSql(sql)
|
|
45
|
+
if (!normalizedSql) {
|
|
46
|
+
throw new Error('[Plugin SQLite SDK] SQL is required.')
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const response = await channel.send('plugin:sqlite:execute', {
|
|
50
|
+
pluginName,
|
|
51
|
+
sql: normalizedSql,
|
|
52
|
+
params: normalizeParams(params)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
if (!response || typeof response !== 'object' || (response as { success?: unknown }).success !== true) {
|
|
56
|
+
const error =
|
|
57
|
+
response && typeof response === 'object' && 'error' in response
|
|
58
|
+
? String((response as { error?: unknown }).error ?? 'Unknown error')
|
|
59
|
+
: 'Unknown error'
|
|
60
|
+
throw new Error(`[Plugin SQLite SDK] Execute failed: ${error}`)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
rowsAffected: Number((response as { rowsAffected?: unknown }).rowsAffected ?? 0),
|
|
65
|
+
lastInsertRowId:
|
|
66
|
+
typeof (response as { lastInsertRowId?: unknown }).lastInsertRowId === 'number'
|
|
67
|
+
? Math.trunc((response as { lastInsertRowId?: number }).lastInsertRowId ?? 0)
|
|
68
|
+
: null
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
query: async <T extends Record<string, unknown> = Record<string, unknown>>(
|
|
73
|
+
sql: string,
|
|
74
|
+
params?: unknown[]
|
|
75
|
+
): Promise<PluginSqliteQueryResult<T>> => {
|
|
76
|
+
const normalizedSql = normalizeSql(sql)
|
|
77
|
+
if (!normalizedSql) {
|
|
78
|
+
throw new Error('[Plugin SQLite SDK] SQL is required.')
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const response = await channel.send('plugin:sqlite:query', {
|
|
82
|
+
pluginName,
|
|
83
|
+
sql: normalizedSql,
|
|
84
|
+
params: normalizeParams(params)
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
if (!response || typeof response !== 'object' || (response as { success?: unknown }).success !== true) {
|
|
88
|
+
const error =
|
|
89
|
+
response && typeof response === 'object' && 'error' in response
|
|
90
|
+
? String((response as { error?: unknown }).error ?? 'Unknown error')
|
|
91
|
+
: 'Unknown error'
|
|
92
|
+
throw new Error(`[Plugin SQLite SDK] Query failed: ${error}`)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
rows: Array.isArray((response as { rows?: unknown }).rows)
|
|
97
|
+
? ((response as { rows: T[] }).rows)
|
|
98
|
+
: [],
|
|
99
|
+
columns: Array.isArray((response as { columns?: unknown }).columns)
|
|
100
|
+
? ((response as { columns: string[] }).columns)
|
|
101
|
+
: []
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
transaction: async (
|
|
106
|
+
statements: PluginSqliteStatement[]
|
|
107
|
+
): Promise<PluginSqliteTransactionResult> => {
|
|
108
|
+
if (!Array.isArray(statements) || statements.length === 0) {
|
|
109
|
+
throw new Error('[Plugin SQLite SDK] Transaction statements are required.')
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const payload = statements.map((statement) => ({
|
|
113
|
+
sql: normalizeSql(statement.sql),
|
|
114
|
+
params: normalizeParams(statement.params)
|
|
115
|
+
}))
|
|
116
|
+
|
|
117
|
+
if (payload.some((statement) => !statement.sql)) {
|
|
118
|
+
throw new Error('[Plugin SQLite SDK] Each transaction statement must include SQL.')
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const response = await channel.send('plugin:sqlite:transaction', {
|
|
122
|
+
pluginName,
|
|
123
|
+
statements: payload
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
if (!response || typeof response !== 'object' || (response as { success?: unknown }).success !== true) {
|
|
127
|
+
const error =
|
|
128
|
+
response && typeof response === 'object' && 'error' in response
|
|
129
|
+
? String((response as { error?: unknown }).error ?? 'Unknown error')
|
|
130
|
+
: 'Unknown error'
|
|
131
|
+
throw new Error(`[Plugin SQLite SDK] Transaction failed: ${error}`)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const results = Array.isArray((response as { results?: unknown }).results)
|
|
135
|
+
? (response as { results: PluginSqliteExecuteResult[] }).results
|
|
136
|
+
: []
|
|
137
|
+
|
|
138
|
+
return { results }
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
package/plugin/sdk/storage.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { FileDetails, StorageStats, StorageTreeNode } from '../../types/storage'
|
|
2
2
|
import { ensureRendererChannel } from './channel'
|
|
3
|
+
import { usePluginName } from './plugin-info'
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Get the storage for the current plugin.
|
|
@@ -9,12 +10,7 @@ import { ensureRendererChannel } from './channel'
|
|
|
9
10
|
* @returns An object with methods to interact with the storage.
|
|
10
11
|
*/
|
|
11
12
|
export function usePluginStorage() {
|
|
12
|
-
|
|
13
|
-
const pluginName = window.$plugin.name as string
|
|
14
|
-
|
|
15
|
-
if (!pluginName) {
|
|
16
|
-
throw new Error('[Plugin SDK] Cannot determine plugin name. Make sure this is called in a plugin context.')
|
|
17
|
-
}
|
|
13
|
+
const pluginName = usePluginName('[Plugin SDK] Cannot determine plugin name. Make sure this is called in a plugin context.')
|
|
18
14
|
|
|
19
15
|
const channel = ensureRendererChannel('[Plugin Storage] Channel not available. Make sure this is called in a plugin context.')
|
|
20
16
|
|
package/plugin/sdk/system.ts
CHANGED
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
import type { ActiveAppSnapshot } from './types'
|
|
2
|
-
|
|
3
|
-
function ensurePluginChannel() {
|
|
4
|
-
const channel = (window as any)?.$channel
|
|
5
|
-
if (!channel) {
|
|
6
|
-
throw new Error('[Plugin SDK] System channel requires plugin renderer context with $channel available.')
|
|
7
|
-
}
|
|
8
|
-
return channel
|
|
9
|
-
}
|
|
2
|
+
import { useChannel } from './channel'
|
|
10
3
|
|
|
11
4
|
export async function getActiveAppSnapshot(options: { forceRefresh?: boolean } = {}): Promise<ActiveAppSnapshot | null> {
|
|
12
|
-
const channel =
|
|
5
|
+
const channel = useChannel('[Plugin SDK] System channel requires plugin renderer context with $channel available.')
|
|
13
6
|
return channel.send('system:get-active-app', options)
|
|
14
7
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { useChannel } from './channel'
|
|
2
|
+
|
|
3
|
+
export interface TempPluginFileCreateOptions {
|
|
4
|
+
ext?: string
|
|
5
|
+
text?: string
|
|
6
|
+
base64?: string
|
|
7
|
+
prefix?: string
|
|
8
|
+
/**
|
|
9
|
+
* Optional retention in milliseconds.
|
|
10
|
+
* If omitted, the host will apply a default plugin temp retention policy.
|
|
11
|
+
*/
|
|
12
|
+
retentionMs?: number
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface TempPluginFileResult {
|
|
16
|
+
url: string
|
|
17
|
+
sizeBytes: number
|
|
18
|
+
createdAt: number
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function useTempPluginFiles() {
|
|
22
|
+
const channel = useChannel('[Plugin SDK] Temp files require plugin renderer context with $channel available.')
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
async create(options: TempPluginFileCreateOptions): Promise<TempPluginFileResult> {
|
|
26
|
+
const res = await channel.send('temp-file:create', options ?? {})
|
|
27
|
+
if (!res || typeof res !== 'object') {
|
|
28
|
+
throw new Error('[Plugin SDK] temp-file:create returned invalid response')
|
|
29
|
+
}
|
|
30
|
+
return res as TempPluginFileResult
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
async delete(url: string): Promise<boolean> {
|
|
34
|
+
const res = await channel.send('temp-file:delete', { url })
|
|
35
|
+
if (res && typeof res === 'object' && 'success' in res) {
|
|
36
|
+
return Boolean((res as any).success)
|
|
37
|
+
}
|
|
38
|
+
return Boolean(res)
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ITouchSDK } from './index'
|
|
2
|
+
import { hasWindow } from '../../env'
|
|
3
|
+
|
|
4
|
+
const DEFAULT_TOUCH_SDK_ERROR
|
|
5
|
+
= '[TouchSDK] Touch SDK not available. Make sure this is called in a plugin context.'
|
|
6
|
+
|
|
7
|
+
let cachedTouchSDK: ITouchSDK | null = null
|
|
8
|
+
|
|
9
|
+
export function useTouchSDK(errorMessage: string = DEFAULT_TOUCH_SDK_ERROR): ITouchSDK {
|
|
10
|
+
const globalWindow = hasWindow() ? window : undefined
|
|
11
|
+
const windowSdk = (globalWindow as any)?.$touchSDK as ITouchSDK | undefined
|
|
12
|
+
const sdk = windowSdk ?? cachedTouchSDK
|
|
13
|
+
if (!sdk) {
|
|
14
|
+
throw new Error(errorMessage)
|
|
15
|
+
}
|
|
16
|
+
cachedTouchSDK = sdk
|
|
17
|
+
return sdk
|
|
18
|
+
}
|
package/plugin/sdk/types.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import type { ITouchChannel, ITouchClientChannel, StandardChannelData } from '@talex-touch/utils/channel'
|
|
8
8
|
import type { IPluginFeature } from '../index'
|
|
9
|
+
import path from 'node:path'
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Handler signature for plugin channel events.
|
|
@@ -90,6 +91,15 @@ export interface IPluginRendererChannel {
|
|
|
90
91
|
export interface PluginClipboardItem {
|
|
91
92
|
id?: number
|
|
92
93
|
type: 'text' | 'image' | 'files'
|
|
94
|
+
/**
|
|
95
|
+
* Clipboard content:
|
|
96
|
+
* - text: plain string
|
|
97
|
+
* - files: JSON string array
|
|
98
|
+
* - image: defaults to a small preview data URL to keep IPC payload light
|
|
99
|
+
*
|
|
100
|
+
* For images, the original asset URL (tfile://...) may be available at:
|
|
101
|
+
* - `meta.image_original_url`
|
|
102
|
+
*/
|
|
93
103
|
content: string
|
|
94
104
|
thumbnail?: string | null
|
|
95
105
|
rawContent?: string | null
|
|
@@ -144,7 +154,6 @@ export interface PluginClipboardSearchResponse {
|
|
|
144
154
|
pageSize: number
|
|
145
155
|
}
|
|
146
156
|
|
|
147
|
-
|
|
148
157
|
export interface ActiveAppSnapshot {
|
|
149
158
|
identifier: string | null
|
|
150
159
|
displayName: string | null
|
|
@@ -230,6 +239,24 @@ export interface IPluginUtils {
|
|
|
230
239
|
*/
|
|
231
240
|
feature: import('./feature-sdk').FeatureSDK
|
|
232
241
|
|
|
242
|
+
/**
|
|
243
|
+
* MetaOverlay SDK for registering global actions
|
|
244
|
+
* @see {@link MetaSDK}
|
|
245
|
+
*/
|
|
246
|
+
meta: import('./meta-sdk').MetaSDK
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Power SDK for low power status
|
|
250
|
+
* @see {@link PowerSDK}
|
|
251
|
+
*/
|
|
252
|
+
power: import('./power').PowerSDK
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Recommend SDK for registering custom recommendation providers
|
|
256
|
+
* @see {@link RecommendSDK}
|
|
257
|
+
*/
|
|
258
|
+
recommend: import('./recommend').RecommendSDK
|
|
259
|
+
|
|
233
260
|
/**
|
|
234
261
|
* Opens a URL in the default browser
|
|
235
262
|
* @param url - The URL to open
|
|
@@ -496,14 +523,14 @@ export interface IEventManager {
|
|
|
496
523
|
* @param event - The event name to listen for
|
|
497
524
|
* @param callback - The callback function to execute when event is emitted
|
|
498
525
|
*/
|
|
499
|
-
on: (event: string, callback:
|
|
526
|
+
on: (event: string, callback: PluginEventHandler) => void
|
|
500
527
|
|
|
501
528
|
/**
|
|
502
529
|
* Removes an event listener
|
|
503
530
|
* @param event - The event name to stop listening for
|
|
504
531
|
* @param callback - The callback function to remove
|
|
505
532
|
*/
|
|
506
|
-
off: (event: string, callback:
|
|
533
|
+
off: (event: string, callback: PluginEventHandler) => void
|
|
507
534
|
|
|
508
535
|
/**
|
|
509
536
|
* Emits an event with optional arguments
|
|
@@ -513,6 +540,8 @@ export interface IEventManager {
|
|
|
513
540
|
emit: (event: string, ...args: any[]) => void
|
|
514
541
|
}
|
|
515
542
|
|
|
543
|
+
export type PluginEventHandler = (...args: any[]) => void
|
|
544
|
+
|
|
516
545
|
/**
|
|
517
546
|
* Plugin configuration interface
|
|
518
547
|
*
|
|
@@ -644,7 +673,6 @@ export function createStorageManager(
|
|
|
644
673
|
pluginPath: string,
|
|
645
674
|
fse: any,
|
|
646
675
|
): IStorageManager {
|
|
647
|
-
const path = require('node:path')
|
|
648
676
|
const dataPath = path.join(pluginPath, 'data')
|
|
649
677
|
|
|
650
678
|
/**
|
|
@@ -954,6 +982,18 @@ export interface TriggerFeatureRequest {
|
|
|
954
982
|
query?: string
|
|
955
983
|
}
|
|
956
984
|
|
|
985
|
+
/**
|
|
986
|
+
* Register a widget for preview or rendering
|
|
987
|
+
*/
|
|
988
|
+
export interface RegisterWidgetRequest {
|
|
989
|
+
/** Plugin name */
|
|
990
|
+
plugin: string
|
|
991
|
+
/** Feature ID */
|
|
992
|
+
feature: string
|
|
993
|
+
/** Emit update event even if cached */
|
|
994
|
+
emitAsUpdate?: boolean
|
|
995
|
+
}
|
|
996
|
+
|
|
957
997
|
/**
|
|
958
998
|
* Input changed event payload
|
|
959
999
|
*/
|
|
@@ -3,28 +3,31 @@ import type {
|
|
|
3
3
|
BrowserWindowConstructorOptions,
|
|
4
4
|
WebContents,
|
|
5
5
|
} from 'electron'
|
|
6
|
-
import {
|
|
6
|
+
import { useChannel } from '../channel'
|
|
7
7
|
|
|
8
|
-
export function createWindow(
|
|
9
|
-
|
|
8
|
+
export async function createWindow(
|
|
9
|
+
options: BrowserWindowConstructorOptions & { file?: string } & { url?: string },
|
|
10
|
+
): Promise<number> {
|
|
11
|
+
const channel = useChannel('[Plugin SDK] Window creation requires renderer channel.')
|
|
12
|
+
const res = await channel.send('window:new', options)
|
|
10
13
|
if (res.error)
|
|
11
14
|
throw new Error(res.error)
|
|
12
15
|
|
|
13
16
|
return res.id
|
|
14
17
|
}
|
|
15
18
|
|
|
16
|
-
export function toggleWinVisible(id: number, visible?: boolean): boolean {
|
|
17
|
-
const
|
|
19
|
+
export async function toggleWinVisible(id: number, visible?: boolean): Promise<boolean> {
|
|
20
|
+
const channel = useChannel('[Plugin SDK] Window visibility requires renderer channel.')
|
|
21
|
+
const res = await channel.send('window:visible', visible !== undefined ? { id, visible } : { id })
|
|
18
22
|
if (res.error)
|
|
19
23
|
throw new Error(res.error)
|
|
20
24
|
|
|
21
25
|
return res.visible
|
|
22
26
|
}
|
|
23
27
|
|
|
24
|
-
export function setWindowProperty(id: number, property: {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const res = genChannel().sendSync('window:property', { id, property })
|
|
28
|
+
export async function setWindowProperty(id: number, property: WindowProperties): Promise<boolean> {
|
|
29
|
+
const channel = useChannel('[Plugin SDK] Window property requires renderer channel.')
|
|
30
|
+
const res = await channel.send('window:property', { id, property })
|
|
28
31
|
if (res.error)
|
|
29
32
|
throw new Error(res.error)
|
|
30
33
|
|