@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.
Files changed (234) hide show
  1. package/.eslintcache +1 -0
  2. package/__tests__/cloud-sync-sdk.test.ts +442 -0
  3. package/__tests__/icons/icons.test.ts +84 -0
  4. package/__tests__/plugin-sdk-lifecycle.test.ts +130 -0
  5. package/__tests__/power-sdk.test.ts +143 -0
  6. package/__tests__/preset-export-types.test.ts +108 -0
  7. package/__tests__/search/fuzzy-match.test.ts +137 -0
  8. package/__tests__/transport/port-policy.test.ts +44 -0
  9. package/__tests__/transport-domain-sdks.test.ts +152 -0
  10. package/__tests__/types/update.test.ts +67 -0
  11. package/account/account-sdk.ts +915 -0
  12. package/account/index.ts +2 -0
  13. package/account/types.ts +321 -0
  14. package/analytics/client.ts +136 -0
  15. package/analytics/index.ts +2 -0
  16. package/analytics/types.ts +156 -0
  17. package/animation/auto-resize.ts +322 -0
  18. package/animation/window-node.ts +26 -19
  19. package/auth/clerk-types.ts +12 -30
  20. package/auth/index.ts +0 -2
  21. package/auth/useAuthState.ts +6 -14
  22. package/base/index.ts +2 -0
  23. package/base/log-level.ts +105 -0
  24. package/channel/index.ts +170 -69
  25. package/cloud-sync/cloud-sync-sdk.ts +450 -0
  26. package/cloud-sync/index.ts +1 -0
  27. package/common/file-scan-utils.ts +17 -9
  28. package/common/index.ts +4 -0
  29. package/common/logger/index.ts +46 -0
  30. package/common/logger/logger-manager.ts +303 -0
  31. package/common/logger/module-logger.ts +270 -0
  32. package/common/logger/transport-logger.ts +234 -0
  33. package/common/logger/types.ts +93 -0
  34. package/common/search/gather.ts +48 -6
  35. package/common/search/index.ts +8 -0
  36. package/common/storage/constants.ts +13 -0
  37. package/common/storage/entity/app-settings.ts +245 -0
  38. package/common/storage/entity/index.ts +3 -0
  39. package/common/storage/entity/layout-atom-types.ts +147 -0
  40. package/common/storage/entity/openers.ts +1 -0
  41. package/common/storage/entity/preset-cloud-api.ts +132 -0
  42. package/common/storage/entity/preset-export-types.ts +256 -0
  43. package/common/storage/entity/shortcut-settings.ts +1 -0
  44. package/common/storage/shortcut-storage.ts +11 -0
  45. package/common/utils/clone-diagnostics.ts +105 -0
  46. package/common/utils/file.ts +16 -8
  47. package/common/utils/index.ts +6 -2
  48. package/common/utils/payload-preview.ts +173 -0
  49. package/common/utils/polling.ts +167 -13
  50. package/common/utils/safe-path.ts +103 -0
  51. package/common/utils/safe-shell.ts +115 -0
  52. package/common/utils/task-queue.ts +4 -1
  53. package/core-box/builder/tuff-builder.ts +0 -1
  54. package/core-box/index.ts +1 -1
  55. package/core-box/recommendation.ts +38 -1
  56. package/core-box/tuff/tuff-dsl.ts +32 -0
  57. package/electron/download-manager.ts +10 -7
  58. package/electron/env-tool.ts +42 -40
  59. package/electron/index.ts +0 -1
  60. package/env/index.ts +156 -0
  61. package/eslint.config.js +55 -0
  62. package/i18n/index.ts +62 -0
  63. package/i18n/locales/en.json +226 -0
  64. package/i18n/locales/zh.json +226 -0
  65. package/i18n/message-keys.ts +236 -0
  66. package/i18n/resolver.ts +181 -0
  67. package/icons/index.ts +257 -0
  68. package/icons/svg.ts +69 -0
  69. package/index.ts +9 -1
  70. package/intelligence/client.ts +72 -42
  71. package/market/constants.ts +9 -5
  72. package/market/index.ts +1 -1
  73. package/market/types.ts +19 -4
  74. package/package.json +15 -5
  75. package/permission/index.ts +143 -46
  76. package/permission/legacy.ts +26 -0
  77. package/permission/registry.ts +304 -0
  78. package/permission/types.ts +164 -0
  79. package/plugin/channel.ts +68 -39
  80. package/plugin/index.ts +80 -7
  81. package/plugin/install.ts +3 -0
  82. package/plugin/log/types.ts +22 -5
  83. package/plugin/node/logger-manager.ts +11 -3
  84. package/plugin/node/logger.ts +24 -17
  85. package/plugin/preload.ts +25 -2
  86. package/plugin/providers/index.ts +4 -4
  87. package/plugin/providers/market-client.ts +6 -3
  88. package/plugin/providers/npm-provider.ts +22 -7
  89. package/plugin/providers/tpex-provider.ts +22 -8
  90. package/plugin/sdk/box-items.ts +14 -0
  91. package/plugin/sdk/box-sdk.ts +64 -0
  92. package/plugin/sdk/channel.ts +119 -4
  93. package/plugin/sdk/clipboard.ts +26 -12
  94. package/plugin/sdk/cloud-sync.ts +113 -0
  95. package/plugin/sdk/common.ts +19 -11
  96. package/plugin/sdk/core-box.ts +6 -15
  97. package/plugin/sdk/division-box.ts +160 -65
  98. package/plugin/sdk/examples/storage-onDidChange-example.js +5 -2
  99. package/plugin/sdk/feature-sdk.ts +111 -76
  100. package/plugin/sdk/flow.ts +146 -45
  101. package/plugin/sdk/hooks/bridge.ts +13 -6
  102. package/plugin/sdk/hooks/life-cycle.ts +35 -16
  103. package/plugin/sdk/index.ts +14 -3
  104. package/plugin/sdk/intelligence.ts +87 -0
  105. package/plugin/sdk/meta/README.md +179 -0
  106. package/plugin/sdk/meta-sdk.ts +244 -0
  107. package/plugin/sdk/notification.ts +9 -0
  108. package/plugin/sdk/plugin-info.ts +64 -0
  109. package/plugin/sdk/power.ts +155 -0
  110. package/plugin/sdk/recommend.ts +21 -0
  111. package/plugin/sdk/service/index.ts +12 -8
  112. package/plugin/sdk/sqlite.ts +141 -0
  113. package/plugin/sdk/storage.ts +2 -6
  114. package/plugin/sdk/system.ts +2 -9
  115. package/plugin/sdk/temp-files.ts +41 -0
  116. package/plugin/sdk/touch-sdk.ts +18 -0
  117. package/plugin/sdk/types.ts +44 -4
  118. package/plugin/sdk/window/index.ts +12 -9
  119. package/plugin/sdk-version.ts +231 -0
  120. package/preload/renderer.ts +3 -2
  121. package/renderer/hooks/arg-mapper.ts +16 -2
  122. package/renderer/hooks/index.ts +13 -0
  123. package/renderer/hooks/initialize.ts +2 -1
  124. package/renderer/hooks/use-agent-market-sdk.ts +7 -0
  125. package/renderer/hooks/use-agent-market.ts +106 -0
  126. package/renderer/hooks/use-agents-sdk.ts +7 -0
  127. package/renderer/hooks/use-app-sdk.ts +7 -0
  128. package/renderer/hooks/use-channel.ts +33 -4
  129. package/renderer/hooks/use-download-sdk.ts +21 -0
  130. package/renderer/hooks/use-intelligence-sdk.ts +7 -0
  131. package/renderer/hooks/use-intelligence-stats.ts +290 -0
  132. package/renderer/hooks/use-intelligence.ts +55 -214
  133. package/renderer/hooks/use-market-sdk.ts +16 -0
  134. package/renderer/hooks/use-notification-sdk.ts +7 -0
  135. package/renderer/hooks/use-permission-sdk.ts +7 -0
  136. package/renderer/hooks/use-permission.ts +325 -0
  137. package/renderer/hooks/use-platform-sdk.ts +7 -0
  138. package/renderer/hooks/use-plugin-sdk.ts +16 -0
  139. package/renderer/hooks/use-settings-sdk.ts +7 -0
  140. package/renderer/hooks/use-update-sdk.ts +21 -0
  141. package/renderer/index.ts +1 -0
  142. package/renderer/ref.ts +19 -10
  143. package/renderer/shared/components/SharedPluginDetailContent.vue +84 -0
  144. package/renderer/shared/components/SharedPluginDetailHeader.vue +116 -0
  145. package/renderer/shared/components/SharedPluginDetailMetaList.vue +39 -0
  146. package/renderer/shared/components/SharedPluginDetailReadme.vue +45 -0
  147. package/renderer/shared/components/SharedPluginDetailVersions.vue +98 -0
  148. package/renderer/shared/components/index.ts +5 -0
  149. package/renderer/shared/components/shims-vue.d.ts +5 -0
  150. package/renderer/shared/index.ts +2 -0
  151. package/renderer/shared/plugin-detail.ts +62 -0
  152. package/renderer/storage/app-settings.ts +3 -1
  153. package/renderer/storage/base-storage.ts +508 -82
  154. package/renderer/storage/intelligence-storage.ts +31 -40
  155. package/renderer/storage/openers.ts +3 -1
  156. package/renderer/storage/storage-subscription.ts +126 -42
  157. package/renderer/touch-sdk/env.ts +10 -10
  158. package/renderer/touch-sdk/index.ts +114 -18
  159. package/renderer/touch-sdk/terminal.ts +24 -13
  160. package/search/feature-matcher.ts +279 -0
  161. package/search/fuzzy-match.ts +64 -34
  162. package/search/index.ts +10 -0
  163. package/search/levenshtein-utils.ts +17 -11
  164. package/transport/errors.ts +310 -0
  165. package/transport/event/builder.ts +378 -0
  166. package/transport/event/index.ts +7 -0
  167. package/transport/event/types.ts +292 -0
  168. package/transport/events/index.ts +2690 -0
  169. package/transport/events/meta-overlay.ts +79 -0
  170. package/transport/events/types/agents.ts +177 -0
  171. package/transport/events/types/app-index.ts +20 -0
  172. package/transport/events/types/app.ts +475 -0
  173. package/transport/events/types/box-item.ts +222 -0
  174. package/transport/events/types/clipboard.ts +80 -0
  175. package/transport/events/types/core-box.ts +534 -0
  176. package/transport/events/types/device-idle.ts +7 -0
  177. package/transport/events/types/division-box.ts +99 -0
  178. package/transport/events/types/download.ts +115 -0
  179. package/transport/events/types/file-index.ts +84 -0
  180. package/transport/events/types/flow.ts +149 -0
  181. package/transport/events/types/index.ts +70 -0
  182. package/transport/events/types/market.ts +39 -0
  183. package/transport/events/types/meta-overlay.ts +184 -0
  184. package/transport/events/types/notification.ts +140 -0
  185. package/transport/events/types/permission.ts +90 -0
  186. package/transport/events/types/platform.ts +8 -0
  187. package/transport/events/types/plugin.ts +631 -0
  188. package/transport/events/types/sentry.ts +20 -0
  189. package/transport/events/types/storage.ts +208 -0
  190. package/transport/events/types/transport.ts +60 -0
  191. package/transport/events/types/tray.ts +16 -0
  192. package/transport/events/types/update.ts +78 -0
  193. package/transport/index.ts +141 -0
  194. package/transport/main.ts +2 -0
  195. package/transport/prelude.ts +208 -0
  196. package/transport/sdk/constants.ts +29 -0
  197. package/transport/sdk/domains/agents-market.ts +47 -0
  198. package/transport/sdk/domains/agents.ts +62 -0
  199. package/transport/sdk/domains/app.ts +48 -0
  200. package/transport/sdk/domains/disposable.ts +35 -0
  201. package/transport/sdk/domains/download.ts +139 -0
  202. package/transport/sdk/domains/index.ts +13 -0
  203. package/transport/sdk/domains/intelligence.ts +616 -0
  204. package/transport/sdk/domains/market.ts +35 -0
  205. package/transport/sdk/domains/notification.ts +62 -0
  206. package/transport/sdk/domains/permission.ts +85 -0
  207. package/transport/sdk/domains/platform.ts +19 -0
  208. package/transport/sdk/domains/plugin.ts +144 -0
  209. package/transport/sdk/domains/settings.ts +102 -0
  210. package/transport/sdk/domains/update.ts +64 -0
  211. package/transport/sdk/index.ts +60 -0
  212. package/transport/sdk/main-transport.ts +710 -0
  213. package/transport/sdk/main.ts +9 -0
  214. package/transport/sdk/plugin-transport.ts +654 -0
  215. package/transport/sdk/port-policy.ts +38 -0
  216. package/transport/sdk/renderer-transport.ts +1165 -0
  217. package/transport/types.ts +605 -0
  218. package/types/agent.ts +399 -0
  219. package/types/cloud-sync.ts +157 -0
  220. package/types/division-box.ts +31 -31
  221. package/types/download.ts +1 -0
  222. package/types/flow.ts +63 -12
  223. package/types/icon.ts +2 -1
  224. package/types/index.ts +5 -0
  225. package/types/intelligence.ts +166 -173
  226. package/types/modules/base.ts +2 -0
  227. package/types/path-browserify.d.ts +5 -0
  228. package/types/platform.ts +12 -0
  229. package/types/startup-info.ts +32 -0
  230. package/types/touch-app-core.ts +8 -8
  231. package/types/update.ts +94 -1
  232. package/vitest.config.ts +25 -0
  233. package/auth/useClerkConfig.ts +0 -40
  234. 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 { genChannel } from '../../channel'
2
+ import { useChannel } from '../channel'
3
3
 
4
- export function regService(service: IService, handler: Function): boolean {
5
- const res = !!genChannel().sendSync('service:reg', { service: service.name })
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
- return !!genChannel().sendSync('service:unreg', { service: service.name })
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: Function) {
18
- // @ts-ignore
19
- genChannel().regChannel('service:handle', ({ data: _data }) => {
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
+ }
@@ -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
- // @ts-ignore
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
 
@@ -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 = ensurePluginChannel()
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
+ }
@@ -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: Function) => void
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: Function) => void
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 { genChannel } from '../../channel'
6
+ import { useChannel } from '../channel'
7
7
 
8
- export function createWindow(options: BrowserWindowConstructorOptions & { file?: string } & { url?: string }): number {
9
- const res = genChannel().sendSync('window:new', options)
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 res = genChannel().sendSync('window:visible', visible !== undefined ? { id, visible } : { id })
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
- }): boolean {
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