@talex-touch/utils 1.0.29 → 1.0.30

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.
@@ -0,0 +1,56 @@
1
+ export interface ClerkUser {
2
+ id: string
3
+ emailAddresses: Array<{
4
+ emailAddress: string
5
+ id: string
6
+ }>
7
+ firstName?: string
8
+ lastName?: string
9
+ username?: string
10
+ imageUrl?: string
11
+ createdAt: string
12
+ updatedAt: string
13
+ }
14
+
15
+ export interface ClerkAuthState {
16
+ isLoaded: boolean
17
+ isSignedIn: boolean
18
+ user: ClerkUser | null
19
+ sessionId: string | null
20
+ }
21
+
22
+ export interface LoginOptions {
23
+ onSuccess?: (user: any) => void
24
+ onError?: (error: any) => void
25
+ }
26
+
27
+ export interface LoginResult {
28
+ success: boolean
29
+ user?: any
30
+ error?: any
31
+ }
32
+
33
+ export interface CurrentUser {
34
+ id: string
35
+ name: string
36
+ email: string
37
+ avatar?: string
38
+ provider: string
39
+ }
40
+
41
+ /**
42
+ * Clerk Auth Config
43
+ */
44
+ export interface ClerkConfig {
45
+ publishableKey: string
46
+ domain?: string
47
+ signInUrl?: string
48
+ signUpUrl?: string
49
+ afterSignInUrl?: string
50
+ afterSignUpUrl?: string
51
+ }
52
+
53
+ export type ClerkResourceSnapshot = {
54
+ user?: any | null
55
+ session?: { id?: string | null } | null
56
+ }
package/auth/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ export * from './clerk-types'
2
+ export * from './useClerkConfig'
3
+ export * from './useClerkProvider'
4
+ export * from './useAuthState'
File without changes
@@ -0,0 +1,44 @@
1
+ import { createGlobalState } from '@vueuse/core'
2
+ import { shallowReactive } from 'vue'
3
+ import { ClerkAuthState, CurrentUser } from './clerk-types'
4
+
5
+ export const useAuthState = createGlobalState(() => {
6
+ const authState = shallowReactive<ClerkAuthState>({
7
+ isLoaded: false,
8
+ isSignedIn: false,
9
+ user: null,
10
+ sessionId: null
11
+ })
12
+
13
+ return { authState }
14
+ })
15
+
16
+ export function useCurrentUser() {
17
+ const { authState } = useAuthState()
18
+
19
+ const currentUser = computed((): CurrentUser | null => {
20
+ if (!authState.isSignedIn || !authState.user) {
21
+ return null
22
+ }
23
+
24
+ const { firstName, lastName, username, imageUrl } = authState.user
25
+ let name = ''
26
+ if (firstName || lastName) {
27
+ name = [firstName, lastName].filter(Boolean).join(' ')
28
+ } else {
29
+ name = username || ''
30
+ }
31
+
32
+ const email = authState.user.emailAddresses?.[0]?.emailAddress || ''
33
+
34
+ return {
35
+ id: authState.user.id,
36
+ name,
37
+ email,
38
+ avatar: imageUrl,
39
+ provider: 'clerk'
40
+ }
41
+ })
42
+
43
+ return { currentUser, authState }
44
+ }
@@ -0,0 +1,40 @@
1
+ import { ClerkConfig } from "./clerk-types"
2
+
3
+ const clerkPublishableKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY
4
+ const clerkDomain = import.meta.env.VITE_CLERK_DOMAIN
5
+
6
+ if (!clerkPublishableKey?.length) {
7
+ throw new Error('VITE_CLERK_PUBLISHABLE_KEY is not set')
8
+ }
9
+
10
+ export const CLERK_PUBLISHABLE_KEY_KEY = 'clerk-publishable-key'
11
+
12
+ export const defaultClerkConfig: ClerkConfig = {
13
+ publishableKey: clerkPublishableKey,
14
+ domain: clerkDomain,
15
+ signInUrl: '/sign-in',
16
+ signUpUrl: '/sign-up',
17
+ afterSignInUrl: '/home',
18
+ afterSignUpUrl: '/home'
19
+ }
20
+
21
+ export function useClerkConfig() {
22
+ const getClerkConfig = (): ClerkConfig => {
23
+ return {
24
+ ...defaultClerkConfig,
25
+ publishableKey:
26
+ localStorage.getItem(CLERK_PUBLISHABLE_KEY_KEY) || defaultClerkConfig.publishableKey
27
+ }
28
+ }
29
+
30
+ const setClerkConfig = (config: Partial<ClerkConfig>): void => {
31
+ if (config.publishableKey) {
32
+ localStorage.setItem(CLERK_PUBLISHABLE_KEY_KEY, config.publishableKey)
33
+ }
34
+ }
35
+
36
+ return {
37
+ getClerkConfig,
38
+ setClerkConfig
39
+ }
40
+ }
@@ -0,0 +1,51 @@
1
+ import { Clerk } from '@clerk/clerk-js'
2
+ import { useClerkConfig } from './useClerkConfig'
3
+
4
+ let clerkInstance: Clerk | null = null
5
+
6
+ export function useClerkProvider() {
7
+ const initializeClerk = async (): Promise<Clerk> => {
8
+ if (clerkInstance) {
9
+ return clerkInstance
10
+ }
11
+
12
+ const { getClerkConfig } = useClerkConfig()
13
+ const config = getClerkConfig()
14
+
15
+ if (!config.publishableKey) {
16
+ throw new Error('Clerk publishable key is required')
17
+ }
18
+
19
+ try {
20
+ clerkInstance = new Clerk(config.publishableKey)
21
+ await clerkInstance.load()
22
+
23
+ console.log('Clerk initialized successfully')
24
+ return clerkInstance
25
+ } catch (error) {
26
+ console.error('Failed to initialize Clerk:', error)
27
+ throw error
28
+ }
29
+ }
30
+
31
+ const getClerk = (): Clerk | null => {
32
+ return clerkInstance
33
+ }
34
+
35
+ const isClerkInitialized = (): boolean => {
36
+ return clerkInstance !== null
37
+ }
38
+
39
+ const cleanupClerk = (): void => {
40
+ if (clerkInstance) {
41
+ clerkInstance = null
42
+ }
43
+ }
44
+
45
+ return {
46
+ initializeClerk,
47
+ getClerk,
48
+ isClerkInitialized,
49
+ cleanupClerk
50
+ }
51
+ }
@@ -14,7 +14,7 @@ const _appSettingOriginData = {
14
14
  },
15
15
  lang: {
16
16
  followSystem: true,
17
- locale: 0,
17
+ locale: 'zh-CN',
18
18
  },
19
19
  keyBind: {
20
20
  summon: 'CTRL + E',
@@ -140,7 +140,7 @@ The core interface for all search results:
140
140
  interface ISearchItem {
141
141
  name: string; // Display name
142
142
  desc: string; // Description
143
- icon: IPluginIcon; // Icon configuration
143
+ icon: ITuffIcon; // Icon configuration
144
144
  push: boolean; // Push mode support
145
145
  names: string[]; // Searchable names
146
146
  keyWords: string[]; // Search keywords
@@ -16,7 +16,7 @@
16
16
  * @module core-box/tuff-dsl
17
17
  */
18
18
 
19
- import { TalexTouch } from "packages/utils/types";
19
+ // import { TalexTouch } from "packages/utils/types";
20
20
 
21
21
  /**
22
22
  * 定义高亮范围
@@ -375,55 +375,15 @@ export interface TuffCustomRender {
375
375
  scripts?: string[];
376
376
  }
377
377
 
378
+ import type { ITuffIcon } from '../../types/icon'
379
+
378
380
  /**
379
- * 图标定义
381
+ * Icon definition
380
382
  *
381
383
  * @description
382
- * 支持多种图标类型,从简单的emoji到复杂的组件。
383
- * 可以是简单字符串或包含详细配置的对象。
384
+ * Unified icon type supporting only ITuffIcon object format
384
385
  */
385
- export type TuffIcon =
386
- | string // 简单字符串:emoji、URL、组件名
387
- | {
388
- /**
389
- * 图标类型
390
- * @description 指定图标的数据格式和来源
391
- * @required
392
- */
393
- type: 'emoji' | 'url' | 'base64' | 'fluent' | 'component';
394
-
395
- /**
396
- * 图标值
397
- * @description 根据type不同,可能是emoji字符、URL地址、Base64编码或组件名
398
- * @required
399
- */
400
- value: string;
401
-
402
- /**
403
- * 备用图标
404
- * @description 当主图标无法加载时显示的替代图标
405
- */
406
- fallback?: string;
407
-
408
- /**
409
- * 动态加载函数
410
- * @description 用于异步加载图标资源的函数
411
- */
412
- loader?: () => Promise<string>;
413
-
414
- /**
415
- * 样式配置
416
- * @description 控制图标的视觉效果
417
- */
418
- style?: {
419
- /** 图标尺寸 */
420
- size?: number;
421
- /** 图标颜色 */
422
- color?: string;
423
- /** 动画效果 */
424
- animation?: 'spin' | 'pulse' | 'bounce';
425
- };
426
- };
386
+ export type TuffIcon = ITuffIcon
427
387
 
428
388
  /**
429
389
  * 标签定义
package/index.ts CHANGED
@@ -1,8 +1,12 @@
1
+ export * from './auth'
1
2
  export * from './base'
2
3
  export * from './common'
3
4
  export * from './plugin'
4
5
  export * from './core-box'
5
6
  export * from './channel'
6
7
  export * from './types'
8
+ export * from './types/icon'
7
9
  export * from './eventbus'
8
10
  export * from './preload'
11
+ export * from './types/download'
12
+ export * from './types/update'
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "module": "./index.ts",
6
6
  "license": "MPL-2.0",
7
7
  "private": false,
8
- "version": "1.0.29",
8
+ "version": "1.0.30",
9
9
  "scripts": {
10
10
  "publish": "npm publish --access public"
11
11
  },
package/plugin/index.ts CHANGED
@@ -14,6 +14,9 @@ export enum PluginStatus {
14
14
  LOADING,
15
15
  LOADED,
16
16
  LOAD_FAILED,
17
+
18
+ DEV_DISCONNECTED, // Dev Server 断连
19
+ DEV_RECONNECTING, // 正在重连
17
20
  }
18
21
 
19
22
  export interface PluginIssue {
@@ -26,14 +29,16 @@ export interface PluginIssue {
26
29
  timestamp?: number
27
30
  }
28
31
 
29
- export interface IPluginIcon {
30
- type: string | 'remixicon' | 'class'
31
- value: any
32
- _value?: string
33
-
34
- init(): Promise<void>
32
+ export interface DevServerHealthCheckResult {
33
+ healthy: boolean
34
+ version?: string
35
+ timestamp: number
36
+ error?: string
35
37
  }
36
38
 
39
+ import type { ITuffIcon } from '../types/icon'
40
+
41
+
37
42
  export interface IPlatformInfo {
38
43
  enable: boolean
39
44
  arch: Arch[]
@@ -51,7 +56,7 @@ export interface IPluginBaseInfo {
51
56
  readme: string
52
57
  version: string
53
58
  desc: string
54
- icon: IPluginIcon
59
+ icon: ITuffIcon
55
60
  platforms: IPlatform
56
61
  _uniqueChannelKey: string
57
62
  }
@@ -134,7 +139,7 @@ export interface IPluginFeature {
134
139
  id: string
135
140
  name: string
136
141
  desc: string
137
- icon: IPluginIcon
142
+ icon: ITuffIcon
138
143
  push: boolean
139
144
  platform: IPlatform
140
145
  commands: IFeatureCommand[]
@@ -323,11 +328,14 @@ export interface IPluginManager {
323
328
  pluginPath: string
324
329
  watcher: FSWatcher | null
325
330
  devWatcher: any // Temporarily any, as DevPluginWatcher is internal to core-app
331
+ healthMonitor: any | null // DevServerHealthMonitor instance, set by PluginModule
326
332
 
327
333
  getPluginList(): Array<object>
328
334
  setActivePlugin(pluginName: string): boolean
329
335
  hasPlugin(name: string): boolean
330
336
  getPluginByName(name: string): ITouchPlugin | undefined
337
+ enablePlugin(pluginName: string): Promise<boolean>
338
+ disablePlugin(pluginName: string): Promise<boolean>
331
339
  reloadPlugin(pluginName: string): Promise<void>
332
340
  persistEnabledPlugins(): Promise<void>
333
341
  listPlugins(): Promise<Array<string>>
@@ -121,7 +121,7 @@ export interface IFeaturesManager {
121
121
  * ```
122
122
  */
123
123
  export function createFeaturesManager(
124
- pluginName: string,
124
+ _pluginName: string,
125
125
  utils: any
126
126
  ): IFeaturesManager {
127
127
  return {
@@ -22,4 +22,4 @@ export * from './clipboard'
22
22
  export * from './core-box'
23
23
  export * from './storage'
24
24
  export * from './system'
25
- export * from './features'
25
+ export { createFeaturesManager, useFeatures } from './features'
@@ -1,3 +1,5 @@
1
+ import type { StorageStats, StorageTreeNode, FileDetails } from '../../types/storage'
2
+
1
3
  /**
2
4
  * Get the storage for the current plugin.
3
5
  * It provides simple file-based storage that is persisted across application launches.
@@ -50,6 +52,68 @@ export function usePluginStorage() {
50
52
  */
51
53
  listFiles: async (): Promise<string[]> => {
52
54
  return channel.send('plugin:storage:list-files', { pluginName })
55
+ },
56
+
57
+ /**
58
+ * Gets storage statistics for the current plugin.
59
+ * @returns A promise that resolves with storage statistics.
60
+ */
61
+ getStats: async (): Promise<StorageStats> => {
62
+ return channel.send('plugin:storage:get-stats', { pluginName })
63
+ },
64
+
65
+ /**
66
+ * Gets the directory tree structure of plugin storage.
67
+ * @returns A promise that resolves with the tree structure.
68
+ */
69
+ getTree: async (): Promise<StorageTreeNode[]> => {
70
+ return channel.send('plugin:storage:get-tree', { pluginName })
71
+ },
72
+
73
+ /**
74
+ * Gets detailed information about a specific file.
75
+ * @param fileName The name of the file to get details for.
76
+ * @returns A promise that resolves with file details.
77
+ */
78
+ getFileDetails: async (fileName: string): Promise<FileDetails | null> => {
79
+ return channel.send('plugin:storage:get-file-details', { pluginName, fileName })
80
+ },
81
+
82
+ /**
83
+ * Clears all storage for the current plugin.
84
+ * @returns A promise that resolves with the operation result.
85
+ */
86
+ clearAll: async (): Promise<{ success: boolean, error?: string }> => {
87
+ return channel.send('plugin:storage:clear', { pluginName })
88
+ },
89
+
90
+ /**
91
+ * Opens the plugin storage folder in the system file manager.
92
+ * @returns A promise that resolves when the folder is opened.
93
+ */
94
+ openFolder: async (): Promise<void> => {
95
+ await channel.send('plugin:storage:open-folder', { pluginName })
96
+ },
97
+
98
+ /**
99
+ * Listens for changes to the storage.
100
+ * @param fileName The file name to listen for changes
101
+ * @param callback The function to call when the storage changes for the current plugin.
102
+ * @returns A function to unsubscribe from the listener.
103
+ */
104
+ onDidChange: (fileName: string, callback: (newConfig: any) => void) => {
105
+ const listener = (data: { name: string, fileName?: string }) => {
106
+ if (data.name === pluginName &&
107
+ (data.fileName === fileName || data.fileName === undefined)) {
108
+ callback(data)
109
+ }
110
+ }
111
+
112
+ channel.regChannel('plugin:storage:update', listener)
113
+
114
+ return () => {
115
+ channel.unRegChannel('plugin:storage:update', listener)
116
+ }
53
117
  }
54
118
  }
55
119
  }
@@ -842,3 +842,78 @@ export interface IPluginInfoManager {
842
842
  */
843
843
  getPlatforms(): any
844
844
  }
845
+
846
+ /**
847
+ * Plugin state change event types
848
+ *
849
+ * @description
850
+ * Represents different types of plugin state changes for incremental updates
851
+ */
852
+ export type PluginStateEvent =
853
+ | { type: 'added'; plugin: any }
854
+ | { type: 'removed'; name: string }
855
+ | { type: 'updated'; name: string; changes: Partial<any> }
856
+ | { type: 'status-changed'; name: string; status: number }
857
+ | { type: 'readme-updated'; name: string; readme: string }
858
+
859
+ /**
860
+ * Plugin filter options for list queries
861
+ */
862
+ export interface PluginFilters {
863
+ /** Filter by plugin status */
864
+ status?: number
865
+ /** Filter by enabled state */
866
+ enabled?: boolean
867
+ /** Filter by development mode */
868
+ dev?: boolean
869
+ }
870
+
871
+ /**
872
+ * Trigger feature request payload
873
+ */
874
+ export interface TriggerFeatureRequest {
875
+ /** Plugin name */
876
+ plugin: string
877
+ /** Feature ID */
878
+ feature: string
879
+ /** Search query or trigger input */
880
+ query?: string
881
+ }
882
+
883
+ /**
884
+ * Input changed event payload
885
+ */
886
+ export interface InputChangedRequest {
887
+ /** Plugin name */
888
+ plugin: string
889
+ /** Feature ID */
890
+ feature: string
891
+ /** Current input value */
892
+ query: string
893
+ }
894
+
895
+ /**
896
+ * Plugin install request payload
897
+ */
898
+ export interface InstallRequest {
899
+ /** Install source (URL, file path, etc) */
900
+ source: string
901
+ /** Hint type for installer */
902
+ hintType?: string
903
+ /** Additional metadata */
904
+ metadata?: Record<string, any>
905
+ /** Client metadata */
906
+ clientMetadata?: Record<string, any>
907
+ }
908
+
909
+ /**
910
+ * Plugin install response
911
+ */
912
+ export interface InstallResponse {
913
+ /** Whether installation was successful */
914
+ success: boolean
915
+ /** Error message if failed */
916
+ error?: string
917
+ /** Installed plugin name */
918
+ pluginName?: string
919
+ }
@@ -2,7 +2,7 @@ import type { LoadingEvent, LoadingMode, LoadingState, PreloadAPI } from './load
2
2
 
3
3
  function getPreloadApi(): PreloadAPI | null {
4
4
  if (typeof window === 'undefined') return null
5
- return window.api ?? null
5
+ return (window as any).api ?? null
6
6
  }
7
7
 
8
8
  export function sendPreloadEvent(event: LoadingEvent): void {
@@ -1,2 +1,3 @@
1
1
  export * from './arg-mapper'
2
2
  export * from './initialize'
3
+ export * from './performance'
@@ -196,3 +196,8 @@ export function refreshPerformanceInfo(): IInitializationInfo {
196
196
  }
197
197
  return window.$initInfo
198
198
  }
199
+
200
+ /**
201
+ * Development mode flag
202
+ */
203
+ export const isDev = import.meta.env.MODE === 'development'
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Renderer Process Performance Monitoring
3
+ *
4
+ * Collects detailed performance metrics in the renderer process
5
+ */
6
+
7
+ /**
8
+ * Renderer performance metrics
9
+ */
10
+ export interface RendererPerformanceMetrics {
11
+ /** Performance timing origin */
12
+ timeOrigin: number
13
+ /** Navigation start time */
14
+ navigationStart: number
15
+ /** DOM content loaded event end */
16
+ domContentLoadedEventEnd?: number
17
+ /** Load event end */
18
+ loadEventEnd?: number
19
+ /** DOM interactive time */
20
+ domInteractive?: number
21
+ /** First paint time */
22
+ firstPaint?: number
23
+ /** First contentful paint time */
24
+ firstContentfulPaint?: number
25
+ }
26
+
27
+ /**
28
+ * Get current renderer performance metrics
29
+ */
30
+ export function getRendererPerformanceMetrics(): RendererPerformanceMetrics {
31
+ const navEntry = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming
32
+ const paintEntries = performance.getEntriesByType('paint')
33
+
34
+ const firstPaint = paintEntries.find(e => e.name === 'first-paint')
35
+ const firstContentfulPaint = paintEntries.find(e => e.name === 'first-contentful-paint')
36
+
37
+ return {
38
+ timeOrigin: performance.timeOrigin,
39
+ navigationStart: performance.timeOrigin,
40
+ domContentLoadedEventEnd: navEntry?.domContentLoadedEventEnd,
41
+ loadEventEnd: navEntry?.loadEventEnd,
42
+ domInteractive: navEntry?.domInteractive,
43
+ firstPaint: firstPaint?.startTime,
44
+ firstContentfulPaint: firstContentfulPaint?.startTime
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Wait for page load and collect metrics
50
+ */
51
+ export function collectPerformanceMetricsOnLoad(): Promise<RendererPerformanceMetrics> {
52
+ return new Promise((resolve) => {
53
+ if (document.readyState === 'complete') {
54
+ // Page already loaded
55
+ setTimeout(() => {
56
+ resolve(getRendererPerformanceMetrics())
57
+ }, 0)
58
+ } else {
59
+ // Wait for load event
60
+ window.addEventListener('load', () => {
61
+ setTimeout(() => {
62
+ resolve(getRendererPerformanceMetrics())
63
+ }, 0)
64
+ }, { once: true })
65
+ }
66
+ })
67
+ }
68
+
69
+ /**
70
+ * Get performance summary for display
71
+ */
72
+ export function getPerformanceSummary(): {
73
+ domReady: number
74
+ pageLoad: number
75
+ firstPaint: number | undefined
76
+ firstContentfulPaint: number | undefined
77
+ } {
78
+ const metrics = getRendererPerformanceMetrics()
79
+
80
+ return {
81
+ domReady: metrics.domContentLoadedEventEnd || 0,
82
+ pageLoad: metrics.loadEventEnd || 0,
83
+ firstPaint: metrics.firstPaint,
84
+ firstContentfulPaint: metrics.firstContentfulPaint
85
+ }
86
+ }
87
+
@@ -32,5 +32,3 @@ class AppSettingsStorage extends TouchStorage<AppSetting> {
32
32
  * Global instance of the application settings
33
33
  */
34
34
  export const appSettings = new AppSettingsStorage();
35
-
36
- console.log(appSettings)
@@ -327,7 +327,6 @@ export class TouchStorage<T extends object> {
327
327
  }
328
328
 
329
329
  const result = channel.sendSync('storage:get', this.#qualifiedName)
330
- console.log("result", result)
331
330
  const parsed = result ? (result as Partial<T>) : {};
332
331
  this.assignData(parsed, true);
333
332
 
package/search/types.ts CHANGED
@@ -16,7 +16,8 @@
16
16
  * @version 1.0.0
17
17
  */
18
18
 
19
- import type { IPluginIcon, IFeatureCommand } from '../plugin';
19
+ import type { IFeatureCommand } from '../plugin';
20
+ import type { ITuffIcon } from '../types/icon';
20
21
 
21
22
  /**
22
23
  * Search Result Item Interface
@@ -59,9 +60,9 @@ export interface ISearchItem {
59
60
  * Defines how the item's icon should be displayed in the UI.
60
61
  * Supports various icon types including files, remix icons, and data URLs.
61
62
  *
62
- * @see {@link IPluginIcon} for icon configuration options
63
+ * @see {@link ITuffIcon} for icon configuration options
63
64
  */
64
- icon: IPluginIcon;
65
+ icon: ITuffIcon;
65
66
 
66
67
  /**
67
68
  * Whether this item supports push mode functionality
@@ -592,7 +593,7 @@ export function createDataItem(options: {
592
593
  icon: {
593
594
  type: iconType,
594
595
  value: iconValue,
595
- init: async () => {} // Required by IPluginIcon interface
596
+ init: async () => {} // Required by ITuffIcon interface
596
597
  },
597
598
  push: false, // Data items don't support push mode
598
599
  names: [name], // Include name in searchable names
@@ -675,7 +676,7 @@ export function createSearchItem(options: {
675
676
  /** Description or subtitle text */
676
677
  desc: string;
677
678
  /** Icon configuration object */
678
- icon: IPluginIcon;
679
+ icon: ITuffIcon;
679
680
  /** Name of the plugin creating this item */
680
681
  pluginName: string;
681
682
  /** Plugin type (defaults to "feature") */
@@ -0,0 +1,162 @@
1
+ // 下载优先级枚举
2
+ export enum DownloadPriority {
3
+ CRITICAL = 100, // 用户手动触发
4
+ HIGH = 80, // 插件安装
5
+ NORMAL = 50, // 应用更新
6
+ LOW = 20, // 资源文件
7
+ BACKGROUND = 10 // 后台预加载
8
+ }
9
+
10
+ // 下载模块枚举
11
+ export enum DownloadModule {
12
+ APP_UPDATE = 'app_update',
13
+ PLUGIN_INSTALL = 'plugin_install',
14
+ RESOURCE_DOWNLOAD = 'resource_download',
15
+ USER_MANUAL = 'user_manual'
16
+ }
17
+
18
+ // 下载状态枚举
19
+ export enum DownloadStatus {
20
+ PENDING = 'pending', // 等待中
21
+ DOWNLOADING = 'downloading', // 下载中
22
+ PAUSED = 'paused', // 已暂停
23
+ COMPLETED = 'completed', // 已完成
24
+ FAILED = 'failed', // 失败
25
+ CANCELLED = 'cancelled' // 已取消
26
+ }
27
+
28
+ // 切片状态枚举
29
+ export enum ChunkStatus {
30
+ PENDING = 'pending',
31
+ DOWNLOADING = 'downloading',
32
+ COMPLETED = 'completed',
33
+ FAILED = 'failed'
34
+ }
35
+
36
+ // 下载任务请求接口
37
+ export interface DownloadRequest {
38
+ id?: string
39
+ url: string
40
+ destination: string
41
+ filename?: string
42
+ priority: DownloadPriority
43
+ module: DownloadModule
44
+ metadata?: Record<string, any>
45
+ checksum?: string
46
+ headers?: Record<string, string>
47
+ }
48
+
49
+ // 下载任务实体
50
+ export interface DownloadTask {
51
+ id: string
52
+ url: string
53
+ destination: string
54
+ filename: string
55
+ priority: DownloadPriority
56
+ module: DownloadModule
57
+ status: DownloadStatus
58
+ progress: DownloadProgress
59
+ chunks: ChunkInfo[]
60
+ metadata: Record<string, any>
61
+ createdAt: Date
62
+ updatedAt: Date
63
+ error?: string
64
+ }
65
+
66
+ // 下载进度接口
67
+ export interface DownloadProgress {
68
+ totalSize?: number
69
+ downloadedSize: number
70
+ speed: number // bytes/s
71
+ remainingTime?: number // seconds
72
+ percentage: number // 0-100
73
+ }
74
+
75
+ // 切片信息接口
76
+ export interface ChunkInfo {
77
+ index: number
78
+ start: number
79
+ end: number
80
+ size: number
81
+ downloaded: number
82
+ status: ChunkStatus
83
+ filePath: string
84
+ }
85
+
86
+ // 下载配置接口
87
+ export interface DownloadConfig {
88
+ concurrency: {
89
+ maxConcurrent: number // 最大并发数 (1-10)
90
+ autoAdjust: boolean // 自动调整
91
+ networkAware: boolean // 网络感知
92
+ priorityBased: boolean // 基于优先级
93
+ }
94
+ chunk: {
95
+ size: number // 切片大小
96
+ resume: boolean // 断点续传
97
+ autoRetry: boolean // 自动重试
98
+ maxRetries: number // 最大重试次数
99
+ }
100
+ storage: {
101
+ tempDir: string // 临时目录
102
+ historyRetention: number // 历史保留天数
103
+ autoCleanup: boolean // 自动清理
104
+ }
105
+ network: {
106
+ timeout: number // 超时时间
107
+ retryDelay: number // 重试延迟
108
+ maxRetries: number // 最大重试次数
109
+ }
110
+ }
111
+
112
+ // 网络状态接口
113
+ export interface NetworkStatus {
114
+ speed: number // bytes/s
115
+ latency: number // ms
116
+ stability: number // 0-1
117
+ recommendedConcurrency: number
118
+ }
119
+
120
+ // 队列状态接口
121
+ export interface QueueStatus {
122
+ totalTasks: number
123
+ pendingTasks: number
124
+ activeTasks: number
125
+ completedTasks: number
126
+ failedTasks: number
127
+ }
128
+
129
+ // 下载统计接口
130
+ export interface DownloadStats {
131
+ totalTasks: number
132
+ completedTasks: number
133
+ failedTasks: number
134
+ totalDownloaded: number
135
+ averageSpeed: number
136
+ }
137
+
138
+ // 默认下载配置
139
+ export const defaultDownloadConfig: DownloadConfig = {
140
+ concurrency: {
141
+ maxConcurrent: 3,
142
+ autoAdjust: true,
143
+ networkAware: true,
144
+ priorityBased: true
145
+ },
146
+ chunk: {
147
+ size: 1024 * 1024, // 1MB
148
+ resume: true,
149
+ autoRetry: true,
150
+ maxRetries: 3
151
+ },
152
+ storage: {
153
+ tempDir: '', // 将在运行时设置
154
+ historyRetention: 30,
155
+ autoCleanup: true
156
+ },
157
+ network: {
158
+ timeout: 30000,
159
+ retryDelay: 5000,
160
+ maxRetries: 3
161
+ }
162
+ }
package/types/icon.ts ADDED
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Icon type definitions
3
+ *
4
+ * @description
5
+ * Defines common icon interfaces and types, supporting emoji, url, and file types
6
+ */
7
+
8
+ /**
9
+ * Icon type enumeration
10
+ * @description
11
+ * - emoji: Emoji characters (e.g., "🚀")
12
+ * - url: Remote URL (http/https) or Data URL (data:image/...)
13
+ * - file: Local file path (relative to plugin root directory)
14
+ */
15
+ export type TuffIconType = 'emoji' | 'url' | 'file'
16
+
17
+ /**
18
+ * Icon status enumeration
19
+ * @description
20
+ * - normal: Normal state
21
+ * - loading: Loading state
22
+ * - error: Error state
23
+ */
24
+ export type TuffIconStatus = 'normal' | 'loading' | 'error'
25
+
26
+ /**
27
+ * Common icon interface
28
+ *
29
+ * @description
30
+ * Unified icon data structure supporting three icon types and status management
31
+ */
32
+ export interface ITuffIcon {
33
+ /** Icon type */
34
+ type: TuffIconType
35
+
36
+ /** Icon value */
37
+ value: string
38
+
39
+ /** Icon status (optional) */
40
+ status?: TuffIconStatus
41
+
42
+ /** Error message (when status is error) */
43
+ error?: string
44
+ }
package/types/index.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export * from './touch-app-core'
2
2
  export * from './modules'
3
+ export * from './storage'
@@ -1,4 +1,4 @@
1
- import { ITouchEventBus } from "packages/utils/eventbus";
1
+ import { ITouchEventBus } from "../../eventbus";
2
2
  import { ModuleKey } from ".";
3
3
  import { TalexTouch } from "../touch-app-core";
4
4
  import { ModuleDirectory, ResolvedModuleFileConfig } from "./base";
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Plugin storage statistics
3
+ */
4
+ export interface StorageStats {
5
+ /** Total size in bytes */
6
+ totalSize: number
7
+ /** Number of files (excluding directories) */
8
+ fileCount: number
9
+ /** Number of directories */
10
+ dirCount: number
11
+ /** Maximum size limit in bytes (10MB) */
12
+ maxSize: number
13
+ /** Usage percentage (0-100) */
14
+ usagePercent: number
15
+ }
16
+
17
+ /**
18
+ * Storage tree node representing a file or directory
19
+ */
20
+ export interface StorageTreeNode {
21
+ /** File or directory name */
22
+ name: string
23
+ /** Relative path from storage root */
24
+ path: string
25
+ /** Node type */
26
+ type: 'file' | 'directory'
27
+ /** Size in bytes (for directories, this is the total size of all contained files) */
28
+ size: number
29
+ /** Last modified timestamp */
30
+ modified: number
31
+ /** Child nodes (only for directories) */
32
+ children?: StorageTreeNode[]
33
+ }
34
+
35
+ /**
36
+ * Detailed information about a specific file
37
+ */
38
+ export interface FileDetails {
39
+ /** File name */
40
+ name: string
41
+ /** Relative path from storage root */
42
+ path: string
43
+ /** File size in bytes */
44
+ size: number
45
+ /** Creation timestamp */
46
+ created: number
47
+ /** Last modified timestamp */
48
+ modified: number
49
+ /** File type (extension or detected type) */
50
+ type: string
51
+ /** File content (if available and size permits) */
52
+ content?: any
53
+ /** Whether content was truncated due to size */
54
+ truncated?: boolean
55
+ }
56
+
@@ -0,0 +1,99 @@
1
+ // 更新源类型枚举
2
+ export enum UpdateProviderType {
3
+ GITHUB = 'github',
4
+ OFFICIAL = 'official',
5
+ CUSTOM = 'custom'
6
+ }
7
+
8
+ // 更新源配置接口
9
+ export interface UpdateSourceConfig {
10
+ type: UpdateProviderType
11
+ name: string
12
+ url?: string
13
+ enabled: boolean
14
+ priority: number
15
+ }
16
+
17
+ // 下载资源接口
18
+ export interface DownloadAsset {
19
+ name: string
20
+ url: string
21
+ size: number
22
+ platform: 'win32' | 'darwin' | 'linux'
23
+ arch: 'x64' | 'arm64'
24
+ checksum?: string
25
+ }
26
+
27
+ // GitHub Release接口(兼容GitHub API格式)
28
+ export interface GitHubRelease {
29
+ tag_name: string
30
+ name: string
31
+ published_at: string
32
+ body: string
33
+ assets: DownloadAsset[]
34
+ }
35
+
36
+ // 更新检查结果接口
37
+ export interface UpdateCheckResult {
38
+ hasUpdate: boolean
39
+ release?: GitHubRelease
40
+ error?: string
41
+ source: string
42
+ }
43
+
44
+ // 自定义更新源配置
45
+ export interface CustomUpdateConfig {
46
+ name: string
47
+ url: string
48
+ apiFormat: 'github' | 'custom'
49
+ headers?: Record<string, string>
50
+ }
51
+
52
+ // 应用预览渠道枚举
53
+ export enum AppPreviewChannel {
54
+ MASTER = 'master',
55
+ SNAPSHOT = 'snapshot'
56
+ }
57
+
58
+ // 更新设置配置
59
+ export interface UpdateSettings {
60
+ enabled: boolean
61
+ frequency: 'startup' | 'daily' | 'weekly' | 'manual'
62
+ source: UpdateSourceConfig
63
+ crossChannel: boolean
64
+ ignoredVersions: string[]
65
+ customSources: CustomUpdateConfig[]
66
+ }
67
+
68
+ // 默认更新设置
69
+ export const defaultUpdateSettings: UpdateSettings = {
70
+ enabled: true,
71
+ frequency: 'startup',
72
+ source: {
73
+ type: UpdateProviderType.GITHUB,
74
+ name: 'GitHub Releases',
75
+ url: 'https://api.github.com/repos/talex-touch/tuff/releases',
76
+ enabled: true,
77
+ priority: 1
78
+ },
79
+ crossChannel: false,
80
+ ignoredVersions: [],
81
+ customSources: []
82
+ }
83
+
84
+ // 更新错误类型
85
+ export enum UpdateErrorType {
86
+ NETWORK_ERROR = 'network_error',
87
+ TIMEOUT_ERROR = 'timeout_error',
88
+ API_ERROR = 'api_error',
89
+ PARSE_ERROR = 'parse_error',
90
+ VERSION_ERROR = 'version_error',
91
+ UNKNOWN_ERROR = 'unknown_error'
92
+ }
93
+
94
+ // 更新错误接口
95
+ export interface UpdateError extends Error {
96
+ type: UpdateErrorType
97
+ code?: string
98
+ statusCode?: number
99
+ }