@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,16 @@
1
+ import { onUnmounted } from 'vue'
2
+ import { useTuffTransport } from '../../transport'
3
+ import { createDisposableBag } from '../../transport/sdk'
4
+ import { createMarketSdk } from '../../transport/sdk/domains/market'
5
+
6
+ export function useMarketSdk() {
7
+ const transport = useTuffTransport()
8
+ return createMarketSdk(transport)
9
+ }
10
+
11
+ export function useMarketSdkScope() {
12
+ const sdk = useMarketSdk()
13
+ const disposables = createDisposableBag()
14
+ onUnmounted(() => disposables.dispose())
15
+ return { sdk, disposables }
16
+ }
@@ -0,0 +1,7 @@
1
+ import { createNotificationSdk } from '../../transport/sdk/domains/notification'
2
+ import { useTuffTransport } from '../../transport/sdk/index'
3
+
4
+ export function useNotificationSdk() {
5
+ const transport = useTuffTransport()
6
+ return createNotificationSdk(transport)
7
+ }
@@ -0,0 +1,7 @@
1
+ import { useTuffTransport } from '../../transport'
2
+ import { createPermissionSdk } from '../../transport/sdk/domains/permission'
3
+
4
+ export function usePermissionSdk() {
5
+ const transport = useTuffTransport()
6
+ return createPermissionSdk(transport)
7
+ }
@@ -0,0 +1,325 @@
1
+ /**
2
+ * usePermission - Permission management hooks for renderer process
3
+ */
4
+
5
+ import type {
6
+ PermissionDefinition,
7
+ PermissionGrant,
8
+ PluginPermissionStatus,
9
+ } from '../../permission/types'
10
+ import { computed, onMounted, onUnmounted, ref } from 'vue'
11
+ import { usePermissionSdk } from './use-permission-sdk'
12
+
13
+ /**
14
+ * Hook for managing plugin permissions
15
+ */
16
+ /**
17
+ * @deprecated 请优先使用 usePermissionSdk(),该 hook 仅保留兼容壳。
18
+ */
19
+
20
+ export function usePermission(pluginId: string) {
21
+ const permissionSdk = usePermissionSdk()
22
+ const permissions = ref<PermissionGrant[]>([])
23
+ const loading = ref(true)
24
+ const error = ref<string | null>(null)
25
+
26
+ /**
27
+ * Refresh permissions from main process
28
+ */
29
+ async function refresh(): Promise<void> {
30
+ loading.value = true
31
+ error.value = null
32
+ try {
33
+ const result = await permissionSdk.getPlugin({ pluginId })
34
+ permissions.value = result || []
35
+ }
36
+ catch (e) {
37
+ error.value = (e as Error).message
38
+ }
39
+ finally {
40
+ loading.value = false
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Grant a permission
46
+ */
47
+ async function grant(permissionId: string): Promise<boolean> {
48
+ try {
49
+ const result = await permissionSdk.grant({
50
+ pluginId,
51
+ permissionId,
52
+ grantedBy: 'user',
53
+ })
54
+ if (result?.success) {
55
+ await refresh()
56
+ }
57
+ return result?.success || false
58
+ }
59
+ catch {
60
+ return false
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Revoke a permission
66
+ */
67
+ async function revoke(permissionId: string): Promise<boolean> {
68
+ try {
69
+ const result = await permissionSdk.revoke({
70
+ pluginId,
71
+ permissionId,
72
+ })
73
+ if (result?.success) {
74
+ await refresh()
75
+ }
76
+ return result?.success || false
77
+ }
78
+ catch {
79
+ return false
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Grant multiple permissions
85
+ */
86
+ async function grantMultiple(permissionIds: string[]): Promise<boolean> {
87
+ try {
88
+ const result = await permissionSdk.grantMultiple({
89
+ pluginId,
90
+ permissionIds,
91
+ grantedBy: 'user',
92
+ })
93
+ if (result?.success) {
94
+ await refresh()
95
+ }
96
+ return result?.success || false
97
+ }
98
+ catch {
99
+ return false
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Revoke all permissions
105
+ */
106
+ async function revokeAll(): Promise<boolean> {
107
+ try {
108
+ const result = await permissionSdk.revokeAll({
109
+ pluginId,
110
+ })
111
+ if (result?.success) {
112
+ await refresh()
113
+ }
114
+ return result?.success || false
115
+ }
116
+ catch {
117
+ return false
118
+ }
119
+ }
120
+
121
+ /**
122
+ * Check if a permission is granted
123
+ */
124
+ function isGranted(permissionId: string): boolean {
125
+ return permissions.value.some(p => p.permissionId === permissionId)
126
+ }
127
+
128
+ /**
129
+ * Get granted permission IDs
130
+ */
131
+ const grantedIds = computed(() => permissions.value.map(p => p.permissionId))
132
+
133
+ // Listen for permission updates
134
+ let unsubscribe: (() => void) | null = null
135
+
136
+ onMounted(() => {
137
+ refresh()
138
+ unsubscribe = permissionSdk.onUpdated((payload) => {
139
+ if (payload?.pluginId === pluginId) {
140
+ refresh()
141
+ }
142
+ })
143
+ })
144
+
145
+ onUnmounted(() => {
146
+ unsubscribe?.()
147
+ })
148
+
149
+ return {
150
+ permissions,
151
+ loading,
152
+ error,
153
+ refresh,
154
+ grant,
155
+ revoke,
156
+ grantMultiple,
157
+ revokeAll,
158
+ isGranted,
159
+ grantedIds,
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Hook for getting plugin permission status
165
+ */
166
+ /**
167
+ * @deprecated 请优先使用 usePermissionSdk().getStatus(),该 hook 仅保留兼容壳。
168
+ */
169
+
170
+ export function usePermissionStatus(
171
+ pluginId: string,
172
+ sdkapi: number | undefined,
173
+ declared: { required: string[], optional: string[] },
174
+ ) {
175
+ const permissionSdk = usePermissionSdk()
176
+ const status = ref<PluginPermissionStatus | null>(null)
177
+ const loading = ref(true)
178
+
179
+ async function refresh(): Promise<void> {
180
+ loading.value = true
181
+ try {
182
+ const result = await permissionSdk.getStatus({
183
+ pluginId,
184
+ sdkapi,
185
+ required: declared.required,
186
+ optional: declared.optional,
187
+ })
188
+ status.value = result
189
+ }
190
+ catch {
191
+ status.value = null
192
+ }
193
+ finally {
194
+ loading.value = false
195
+ }
196
+ }
197
+
198
+ // Computed helpers
199
+ const enforcePermissions = computed(() => status.value?.enforcePermissions ?? false)
200
+ const missingRequired = computed(() => status.value?.missingRequired ?? [])
201
+ const hasWarning = computed(() => !!status.value?.warning)
202
+ const warning = computed(() => status.value?.warning)
203
+
204
+ onMounted(() => {
205
+ refresh()
206
+ })
207
+
208
+ return {
209
+ status,
210
+ loading,
211
+ refresh,
212
+ enforcePermissions,
213
+ missingRequired,
214
+ hasWarning,
215
+ warning,
216
+ }
217
+ }
218
+
219
+ /**
220
+ * Hook for getting permission registry (all available permissions)
221
+ */
222
+ /**
223
+ * @deprecated 请优先使用 usePermissionSdk().getRegistry(),该 hook 仅保留兼容壳。
224
+ */
225
+
226
+ export function usePermissionRegistry() {
227
+ const permissionSdk = usePermissionSdk()
228
+ const registry = ref<PermissionDefinition[]>([])
229
+ const loading = ref(true)
230
+
231
+ async function refresh(): Promise<void> {
232
+ loading.value = true
233
+ try {
234
+ const result = await permissionSdk.getRegistry()
235
+ registry.value = result || []
236
+ }
237
+ catch {
238
+ registry.value = []
239
+ }
240
+ finally {
241
+ loading.value = false
242
+ }
243
+ }
244
+
245
+ /**
246
+ * Get permission by ID
247
+ */
248
+ function getPermission(id: string): PermissionDefinition | undefined {
249
+ return registry.value.find(p => p.id === id)
250
+ }
251
+
252
+ /**
253
+ * Get permissions by category
254
+ */
255
+ function byCategory(category: string): PermissionDefinition[] {
256
+ return registry.value.filter(p => p.category === category)
257
+ }
258
+
259
+ /**
260
+ * Get permissions by risk level
261
+ */
262
+ function byRisk(risk: string): PermissionDefinition[] {
263
+ return registry.value.filter(p => p.risk === risk)
264
+ }
265
+
266
+ onMounted(() => {
267
+ refresh()
268
+ })
269
+
270
+ return {
271
+ registry,
272
+ loading,
273
+ refresh,
274
+ getPermission,
275
+ byCategory,
276
+ byRisk,
277
+ }
278
+ }
279
+
280
+ /**
281
+ * Hook for getting all plugin permissions
282
+ */
283
+ /**
284
+ * @deprecated 请优先使用 usePermissionSdk().getAll(),该 hook 仅保留兼容壳。
285
+ */
286
+
287
+ export function useAllPluginPermissions() {
288
+ const permissionSdk = usePermissionSdk()
289
+ const permissions = ref<Record<string, PermissionGrant[]>>({})
290
+ const loading = ref(true)
291
+
292
+ async function refresh(): Promise<void> {
293
+ loading.value = true
294
+ try {
295
+ const result = await permissionSdk.getAll()
296
+ permissions.value = result || {}
297
+ }
298
+ catch {
299
+ permissions.value = {}
300
+ }
301
+ finally {
302
+ loading.value = false
303
+ }
304
+ }
305
+
306
+ // Listen for permission updates
307
+ let unsubscribe: (() => void) | null = null
308
+
309
+ onMounted(() => {
310
+ refresh()
311
+ unsubscribe = permissionSdk.onUpdated(() => {
312
+ refresh()
313
+ })
314
+ })
315
+
316
+ onUnmounted(() => {
317
+ unsubscribe?.()
318
+ })
319
+
320
+ return {
321
+ permissions,
322
+ loading,
323
+ refresh,
324
+ }
325
+ }
@@ -0,0 +1,7 @@
1
+ import { useTuffTransport } from '../../transport'
2
+ import { createPlatformSdk } from '../../transport/sdk/domains/platform'
3
+
4
+ export function usePlatformSdk() {
5
+ const transport = useTuffTransport()
6
+ return createPlatformSdk(transport)
7
+ }
@@ -0,0 +1,16 @@
1
+ import { onUnmounted } from 'vue'
2
+ import { useTuffTransport } from '../../transport'
3
+ import { createDisposableBag } from '../../transport/sdk'
4
+ import { createPluginSdk } from '../../transport/sdk/domains/plugin'
5
+
6
+ export function usePluginSdk() {
7
+ const transport = useTuffTransport()
8
+ return createPluginSdk(transport)
9
+ }
10
+
11
+ export function usePluginSdkScope() {
12
+ const sdk = usePluginSdk()
13
+ const disposables = createDisposableBag()
14
+ onUnmounted(() => disposables.dispose())
15
+ return { sdk, disposables }
16
+ }
@@ -0,0 +1,7 @@
1
+ import { useTuffTransport } from '../../transport'
2
+ import { createSettingsSdk } from '../../transport/sdk/domains/settings'
3
+
4
+ export function useSettingsSdk() {
5
+ const transport = useTuffTransport()
6
+ return createSettingsSdk(transport)
7
+ }
@@ -0,0 +1,21 @@
1
+ import { onUnmounted } from 'vue'
2
+ import { useTuffTransport } from '../../transport'
3
+ import { createDisposableBag } from '../../transport/sdk'
4
+ import { createUpdateSdk } from '../../transport/sdk/domains/update'
5
+
6
+ export function useUpdateSdk() {
7
+ const transport = useTuffTransport()
8
+ return createUpdateSdk(transport)
9
+ }
10
+
11
+ export function useUpdateSdkScope() {
12
+ const sdk = useUpdateSdk()
13
+ const disposables = createDisposableBag()
14
+ const register = (dispose: () => void) => {
15
+ disposables.add(dispose)
16
+ return dispose
17
+ }
18
+
19
+ onUnmounted(() => disposables.dispose())
20
+ return { sdk, disposables, register }
21
+ }
package/renderer/index.ts CHANGED
@@ -8,3 +8,4 @@ export * from './storage'
8
8
 
9
9
  export * from './touch-sdk'
10
10
  export * from './touch-sdk/utils'
11
+ export * from './shared'
package/renderer/ref.ts CHANGED
@@ -1,38 +1,47 @@
1
+ import type { WritableComputedRef } from 'vue'
1
2
  import { computed, customRef } from 'vue'
2
3
 
3
- export function useModelWrapper(props: any, emit: any, name = 'modelValue') {
4
+ export function useModelWrapper<
5
+ P extends Record<string, unknown>,
6
+ K extends string = 'modelValue'
7
+ >(
8
+ props: P,
9
+ emit: (event: `update:${K}`, value: P[K]) => void,
10
+ name?: K
11
+ ): WritableComputedRef<P[K]> {
12
+ const key = (name ?? 'modelValue') as K
4
13
  return computed({
5
- get: () => props[name],
6
- set: value => emit(`update:${name}`, value),
14
+ get: () => props[key] as P[K],
15
+ set: value => emit(`update:${key}` as `update:${K}`, value),
7
16
  })
8
17
  }
9
18
 
10
- export function throttleRef(value: any, time: number) {
19
+ export function throttleRef<T>(value: T, time: number) {
11
20
  let ts = 0
12
21
 
13
- return customRef((track, trigger) => {
22
+ return customRef<T>((track, trigger) => {
14
23
  return {
15
24
  get() {
16
25
  track()
17
26
  return value
18
27
  },
19
28
  set(newValue) {
20
- if (new Date().getTime() - ts < time)
29
+ if (Date.now() - ts < time)
21
30
  return
22
31
 
23
32
  value = newValue
24
33
  track()
25
34
  trigger()
26
- ts = new Date().getTime()
35
+ ts = Date.now()
27
36
  },
28
37
  }
29
38
  })
30
39
  }
31
40
 
32
- export function debounceRef(value: any, delay: number) {
33
- let timer: any
41
+ export function debounceRef<T>(value: T, delay: number) {
42
+ let timer: ReturnType<typeof setTimeout> | undefined
34
43
 
35
- return customRef((track, trigger) => {
44
+ return customRef<T>((track, trigger) => {
36
45
  return {
37
46
  get() {
38
47
  track()
@@ -0,0 +1,84 @@
1
+ <script setup lang="ts">
2
+ import type { SharedPluginDetail } from '../plugin-detail'
3
+ import { computed } from 'vue'
4
+ import SharedPluginDetailHeader from './SharedPluginDetailHeader.vue'
5
+ import SharedPluginDetailMetaList from './SharedPluginDetailMetaList.vue'
6
+ import SharedPluginDetailReadme from './SharedPluginDetailReadme.vue'
7
+ import SharedPluginDetailVersions from './SharedPluginDetailVersions.vue'
8
+
9
+ interface Props {
10
+ detail: SharedPluginDetail
11
+ showMeta?: boolean
12
+ showReadme?: boolean
13
+ showVersions?: boolean
14
+ readmeTitle?: string
15
+ versionsTitle?: string
16
+ metaTitle?: string
17
+ emptyReadmeText?: string
18
+ emptyVersionsText?: string
19
+ renderMarkdown?: (markdown: string) => string
20
+ formatDate?: (value: string | number | Date) => string
21
+ formatNumber?: (value: number) => string
22
+ formatSize?: (value: number) => string
23
+ installsText?: string
24
+ versionText?: string
25
+ updatedText?: string
26
+ officialLabel?: string
27
+ installsLabel?: string
28
+ versionLabel?: string
29
+ updatedLabel?: string
30
+ }
31
+
32
+ const props = withDefaults(defineProps<Props>(), {
33
+ showMeta: true,
34
+ showReadme: true,
35
+ showVersions: true,
36
+ readmeTitle: 'README',
37
+ versionsTitle: 'Versions',
38
+ metaTitle: '',
39
+ emptyReadmeText: 'No README',
40
+ emptyVersionsText: 'No versions'
41
+ })
42
+
43
+ const hasMeta = computed(() => (props.detail.metaItems?.length ?? 0) > 0)
44
+ </script>
45
+
46
+ <template>
47
+ <div class="space-y-6">
48
+ <SharedPluginDetailHeader
49
+ :detail="detail"
50
+ :format-date="formatDate"
51
+ :format-number="formatNumber"
52
+ :installs-text="installsText"
53
+ :version-text="versionText"
54
+ :updated-text="updatedText"
55
+ :official-label="officialLabel"
56
+ :installs-label="installsLabel"
57
+ :version-label="versionLabel"
58
+ :updated-label="updatedLabel"
59
+ />
60
+
61
+ <div class="flex flex-col gap-6 lg:flex-row">
62
+ <div class="min-w-0 flex-1 space-y-6">
63
+ <SharedPluginDetailReadme
64
+ v-if="showReadme"
65
+ :readme="detail.readme"
66
+ :title="readmeTitle"
67
+ :empty-text="emptyReadmeText"
68
+ :render-markdown="renderMarkdown"
69
+ />
70
+ <SharedPluginDetailVersions
71
+ v-if="showVersions"
72
+ :versions="detail.versions"
73
+ :title="versionsTitle"
74
+ :empty-text="emptyVersionsText"
75
+ :format-date="formatDate"
76
+ :format-size="formatSize"
77
+ />
78
+ </div>
79
+ <aside v-if="showMeta && hasMeta" class="w-full lg:w-64">
80
+ <SharedPluginDetailMetaList :items="detail.metaItems" :title="metaTitle" />
81
+ </aside>
82
+ </div>
83
+ </div>
84
+ </template>
@@ -0,0 +1,116 @@
1
+ <script setup lang="ts">
2
+ import type { SharedPluginDetail } from '../plugin-detail'
3
+ import { computed } from 'vue'
4
+
5
+ interface Props {
6
+ detail: SharedPluginDetail
7
+ formatDate?: (value: string | number | Date) => string
8
+ formatNumber?: (value: number) => string
9
+ installsText?: string
10
+ versionText?: string
11
+ updatedText?: string
12
+ officialLabel?: string
13
+ installsLabel?: string
14
+ versionLabel?: string
15
+ updatedLabel?: string
16
+ }
17
+
18
+ const props = withDefaults(defineProps<Props>(), {
19
+ officialLabel: 'Official',
20
+ installsLabel: 'Installs',
21
+ versionLabel: 'Version',
22
+ updatedLabel: 'Updated'
23
+ })
24
+
25
+ const displayInstalls = computed(() => {
26
+ if (props.detail.installs === undefined || props.detail.installs === null) {
27
+ return ''
28
+ }
29
+ return props.formatNumber ? props.formatNumber(props.detail.installs) : `${props.detail.installs}`
30
+ })
31
+
32
+ const installsPillText = computed(() => {
33
+ if (props.installsText) return props.installsText
34
+ if (!displayInstalls.value) return ''
35
+ return `${props.installsLabel} · ${displayInstalls.value}`
36
+ })
37
+
38
+ const updatedAtValue = computed(() => {
39
+ return props.detail.updatedAt ?? props.detail.latestVersion?.createdAt
40
+ })
41
+
42
+ const displayUpdatedAt = computed(() => {
43
+ if (!updatedAtValue.value) return ''
44
+ if (props.formatDate) return props.formatDate(updatedAtValue.value)
45
+ const date = new Date(updatedAtValue.value as any)
46
+ return Number.isNaN(date.valueOf()) ? `${updatedAtValue.value}` : date.toLocaleDateString()
47
+ })
48
+
49
+ const displayVersion = computed(() => props.detail.latestVersion?.version ?? '')
50
+
51
+ const isOfficial = computed(() => props.detail.official || props.detail.trustLevel === 'official')
52
+
53
+ const versionPillText = computed(() => {
54
+ if (props.versionText) return props.versionText
55
+ if (!displayVersion.value) return ''
56
+ return `${props.versionLabel} · v${displayVersion.value}`
57
+ })
58
+
59
+ const updatedPillText = computed(() => {
60
+ if (props.updatedText) return props.updatedText
61
+ if (!displayUpdatedAt.value) return ''
62
+ return `${props.updatedLabel} · ${displayUpdatedAt.value}`
63
+ })
64
+ </script>
65
+
66
+ <template>
67
+ <header class="space-y-3">
68
+ <div class="flex flex-wrap items-center gap-2">
69
+ <h2 class="text-2xl font-semibold">
70
+ {{ detail.name }}
71
+ </h2>
72
+ <span
73
+ v-if="isOfficial"
74
+ class="inline-flex items-center rounded-full bg-emerald-100 px-2 py-0.5 text-xs text-emerald-700"
75
+ >
76
+ {{ officialLabel }}
77
+ </span>
78
+ <span
79
+ v-if="detail.category?.label"
80
+ class="inline-flex items-center rounded-full bg-black/5 px-2 py-0.5 text-xs text-black/70"
81
+ >
82
+ {{ detail.category.label }}
83
+ </span>
84
+ </div>
85
+
86
+ <p v-if="detail.summary" class="text-sm text-black/70">
87
+ {{ detail.summary }}
88
+ </p>
89
+
90
+ <p v-if="detail.author?.name" class="text-xs text-black/50">
91
+ {{ detail.author.name }}
92
+ </p>
93
+
94
+ <div class="flex flex-wrap gap-2 text-xs text-black/60">
95
+ <span v-if="installsPillText" class="inline-flex items-center rounded-full bg-black/5 px-2 py-0.5">
96
+ {{ installsPillText }}
97
+ </span>
98
+ <span v-if="versionPillText" class="inline-flex items-center rounded-full bg-black/5 px-2 py-0.5">
99
+ {{ versionPillText }}
100
+ </span>
101
+ <span v-if="updatedPillText" class="inline-flex items-center rounded-full bg-black/5 px-2 py-0.5">
102
+ {{ updatedPillText }}
103
+ </span>
104
+ </div>
105
+
106
+ <div v-if="detail.badges?.length" class="flex flex-wrap gap-2">
107
+ <span
108
+ v-for="badge in detail.badges"
109
+ :key="badge"
110
+ class="inline-flex items-center rounded-full bg-black/5 px-2 py-0.5 text-xs text-black/70"
111
+ >
112
+ {{ badge }}
113
+ </span>
114
+ </div>
115
+ </header>
116
+ </template>