@talex-touch/utils 1.0.30 → 1.0.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/animation/window-node.ts +205 -0
- package/animation/window.ts +19 -15
- package/auth/clerk-types.ts +1 -1
- package/auth/index.ts +1 -1
- package/auth/useAuthState.ts +6 -5
- package/auth/useClerkConfig.ts +6 -6
- package/auth/useClerkProvider.ts +3 -2
- package/channel/index.ts +28 -21
- package/common/file-scan-constants.ts +137 -121
- package/common/file-scan-utils.ts +49 -25
- package/common/index.ts +3 -3
- package/common/search/gather.ts +1 -1
- package/common/search/index.ts +5 -6
- package/common/storage/constants.ts +3 -2
- package/common/storage/entity/app-settings.ts +19 -3
- package/common/storage/entity/shortcut-settings.ts +10 -10
- package/common/storage/shortcut-storage.ts +6 -4
- package/common/utils/file.ts +15 -4
- package/common/utils/index.ts +62 -52
- package/common/utils/polling.ts +114 -63
- package/common/utils/task-queue.ts +11 -10
- package/common/utils/time.ts +50 -47
- package/common/utils/timing.ts +41 -37
- package/core-box/builder/index.ts +1 -1
- package/core-box/builder/tuff-builder.ts +255 -230
- package/core-box/index.ts +3 -6
- package/core-box/preview/index.ts +1 -0
- package/core-box/preview/types.ts +43 -0
- package/core-box/tuff/index.ts +1 -1
- package/core-box/tuff/tuff-dsl.ts +419 -253
- package/electron/clipboard-helper.ts +20 -12
- package/electron/download-manager.ts +43 -42
- package/electron/env-tool.ts +19 -18
- package/electron/file-parsers/index.ts +2 -2
- package/electron/file-parsers/parsers/text-parser.ts +15 -14
- package/electron/file-parsers/registry.ts +9 -7
- package/electron/file-parsers/types.ts +4 -4
- package/electron/index.ts +1 -1
- package/eventbus/index.ts +11 -11
- package/index.ts +6 -5
- package/intelligence/client.ts +87 -0
- package/intelligence/index.ts +1 -0
- package/package.json +14 -14
- package/permission/index.ts +8 -8
- package/plugin/channel.ts +77 -68
- package/plugin/index.ts +113 -84
- package/plugin/install.ts +8 -8
- package/plugin/log/types.ts +5 -5
- package/plugin/node/index.ts +1 -1
- package/plugin/node/logger-manager.ts +14 -11
- package/plugin/node/logger.ts +8 -8
- package/plugin/plugin-source.ts +11 -11
- package/plugin/preload.ts +6 -3
- package/plugin/providers/registry.ts +8 -7
- package/plugin/providers/types.ts +6 -6
- package/plugin/sdk/channel.ts +20 -20
- package/plugin/sdk/clipboard.ts +8 -6
- package/plugin/sdk/common.ts +10 -6
- package/plugin/sdk/core-box.ts +2 -3
- package/plugin/sdk/division-box.ts +266 -0
- package/plugin/sdk/enum/bridge-event.ts +1 -1
- package/plugin/sdk/examples/storage-onDidChange-example.js +1 -1
- package/plugin/sdk/features.ts +34 -26
- package/plugin/sdk/hooks/bridge.ts +3 -6
- package/plugin/sdk/hooks/index.ts +1 -1
- package/plugin/sdk/hooks/life-cycle.ts +4 -10
- package/plugin/sdk/index.ts +9 -13
- package/plugin/sdk/service/index.ts +3 -3
- package/plugin/sdk/storage.ts +4 -4
- package/plugin/sdk/system.ts +1 -1
- package/plugin/sdk/types.ts +169 -143
- package/plugin/sdk/window/index.ts +8 -5
- package/preload/loading.ts +6 -6
- package/preload/renderer.ts +4 -2
- package/renderer/hooks/arg-mapper.ts +1 -2
- package/renderer/hooks/index.ts +2 -0
- package/renderer/hooks/initialize.ts +10 -8
- package/renderer/hooks/performance.ts +4 -4
- package/renderer/hooks/use-channel.ts +150 -0
- package/renderer/hooks/use-intelligence.ts +236 -0
- package/renderer/index.ts +6 -1
- package/renderer/ref.ts +32 -36
- package/renderer/slots.ts +29 -26
- package/renderer/storage/app-settings.ts +16 -6
- package/renderer/storage/base-storage.ts +236 -88
- package/renderer/storage/index.ts +3 -0
- package/renderer/storage/intelligence-storage.ts +215 -0
- package/renderer/storage/openers.ts +13 -3
- package/renderer/touch-sdk/env.ts +41 -41
- package/renderer/touch-sdk/index.ts +1 -1
- package/renderer/touch-sdk/terminal.ts +5 -5
- package/renderer/touch-sdk/utils.ts +4 -3
- package/search/levenshtein-utils.ts +11 -11
- package/search/types.ts +102 -103
- package/service/index.ts +11 -11
- package/service/protocol/index.ts +217 -14
- package/types/division-box.ts +248 -0
- package/types/download.ts +72 -34
- package/types/icon.ts +2 -1
- package/types/index.ts +3 -1
- package/types/intelligence.ts +413 -0
- package/types/modules/base.ts +16 -16
- package/types/modules/index.ts +1 -1
- package/types/modules/module-lifecycle.ts +21 -21
- package/types/modules/module-manager.ts +11 -11
- package/types/modules/module.ts +16 -16
- package/types/storage.ts +0 -1
- package/types/touch-app-core.ts +32 -32
- package/types/update.ts +79 -21
- package/core-box/README.md +0 -218
- package/core-box/builder/tuff-builder.example.ts.bak +0 -258
- package/core-box/run-tests.sh +0 -7
- package/core-box/search.ts +0 -1
package/plugin/preload.ts
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import type { ITouchClientChannel } from '../channel'
|
|
2
|
+
import type { ITouchSDK } from './sdk/index'
|
|
3
|
+
// Import SDK for side effects (initializes hooks)
|
|
2
4
|
import './sdk/index'
|
|
3
5
|
|
|
4
|
-
// window type
|
|
6
|
+
// window type - includes both plugin preload types and SDK types
|
|
5
7
|
declare global {
|
|
6
|
-
|
|
8
|
+
interface Window {
|
|
7
9
|
$plugin: {
|
|
8
10
|
name: string
|
|
9
|
-
path:
|
|
11
|
+
path: object
|
|
10
12
|
}
|
|
11
13
|
$channel: ITouchClientChannel
|
|
12
14
|
$crash: (message: string, extraData: any) => void
|
|
13
15
|
$config: {
|
|
14
16
|
themeStyle: any
|
|
15
17
|
}
|
|
18
|
+
$touchSDK: ITouchSDK
|
|
16
19
|
}
|
|
17
20
|
}
|
|
18
21
|
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { defaultRiskPromptHandler } from '../risk'
|
|
2
1
|
import type { PluginInstallRequest, PluginInstallResult, PluginProvider, PluginProviderContext } from './types'
|
|
2
|
+
import { defaultRiskPromptHandler } from '../risk'
|
|
3
3
|
|
|
4
4
|
class ProviderRegistry {
|
|
5
5
|
private providers: PluginProvider[] = []
|
|
6
6
|
|
|
7
7
|
register(provider: PluginProvider): void {
|
|
8
|
-
if (this.providers.some(
|
|
8
|
+
if (this.providers.some(item => item.type === provider.type)) {
|
|
9
9
|
throw new Error(`Plugin provider '${provider.type}' already registered`)
|
|
10
10
|
}
|
|
11
11
|
this.providers.push(provider)
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
unregister(type: PluginProvider['type']): void {
|
|
15
|
-
this.providers = this.providers.filter(
|
|
15
|
+
this.providers = this.providers.filter(item => item.type !== type)
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
clear(): void {
|
|
@@ -20,19 +20,20 @@ class ProviderRegistry {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
resolve(request: PluginInstallRequest): PluginProvider | undefined {
|
|
23
|
-
return this.providers.find(
|
|
23
|
+
return this.providers.find(provider => provider.canHandle(request))
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
async install(
|
|
27
27
|
request: PluginInstallRequest,
|
|
28
|
-
context: PluginProviderContext = {}
|
|
28
|
+
context: PluginProviderContext = {},
|
|
29
29
|
): Promise<PluginInstallResult | undefined> {
|
|
30
30
|
const provider = this.resolve(request)
|
|
31
|
-
if (!provider)
|
|
31
|
+
if (!provider)
|
|
32
|
+
return undefined
|
|
32
33
|
|
|
33
34
|
const composedContext: PluginProviderContext = {
|
|
34
35
|
riskPrompt: defaultRiskPromptHandler,
|
|
35
|
-
...context
|
|
36
|
+
...context,
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
if (!composedContext.riskPrompt) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { IDownloadOptions, IDownloadResult } from '../plugin-source'
|
|
2
1
|
import type { IManifest } from '..'
|
|
2
|
+
import type { IDownloadOptions, IDownloadResult } from '../plugin-source'
|
|
3
3
|
import type { RiskPromptHandler } from '../risk'
|
|
4
4
|
|
|
5
5
|
export enum PluginProviderType {
|
|
@@ -7,7 +7,7 @@ export enum PluginProviderType {
|
|
|
7
7
|
NPM = 'npm',
|
|
8
8
|
TPEX = 'tpex',
|
|
9
9
|
FILE = 'file',
|
|
10
|
-
DEV = 'dev'
|
|
10
|
+
DEV = 'dev',
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export interface PluginInstallRequest {
|
|
@@ -43,11 +43,11 @@ export interface PluginInstallResult extends IDownloadResult {
|
|
|
43
43
|
|
|
44
44
|
export interface PluginProvider {
|
|
45
45
|
readonly type: PluginProviderType
|
|
46
|
-
canHandle(request: PluginInstallRequest)
|
|
47
|
-
install(
|
|
46
|
+
canHandle: (request: PluginInstallRequest) => boolean
|
|
47
|
+
install: (
|
|
48
48
|
request: PluginInstallRequest,
|
|
49
|
-
context?: PluginProviderContext
|
|
50
|
-
)
|
|
49
|
+
context?: PluginProviderContext,
|
|
50
|
+
) => Promise<PluginInstallResult>
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
export interface PluginInstallSummary {
|
package/plugin/sdk/channel.ts
CHANGED
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
import type { ITouchClientChannel } from '@talex-touch/utils/channel'
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
1
|
+
import type { ITouchClientChannel } from '@talex-touch/utils/channel'
|
|
2
|
+
import type { IPluginRendererChannel, PluginChannelHandler } from './types'
|
|
3
|
+
import { genChannel } from '../channel'
|
|
4
4
|
|
|
5
|
-
const ensureClientChannel = (): ITouchClientChannel => genChannel()
|
|
5
|
+
const ensureClientChannel = (): ITouchClientChannel => genChannel()
|
|
6
6
|
|
|
7
7
|
export function createPluginRendererChannel(): IPluginRendererChannel {
|
|
8
|
-
const client = ensureClientChannel()
|
|
8
|
+
const client = ensureClientChannel()
|
|
9
9
|
|
|
10
10
|
return {
|
|
11
11
|
send(eventName, payload) {
|
|
12
|
-
return client.send(eventName, payload)
|
|
12
|
+
return client.send(eventName, payload)
|
|
13
13
|
},
|
|
14
14
|
|
|
15
15
|
sendSync(eventName, payload) {
|
|
16
|
-
return client.sendSync(eventName, payload)
|
|
16
|
+
return client.sendSync(eventName, payload)
|
|
17
17
|
},
|
|
18
18
|
|
|
19
19
|
on(eventName, handler) {
|
|
20
|
-
return client.regChannel(eventName, handler)
|
|
20
|
+
return client.regChannel(eventName, handler)
|
|
21
21
|
},
|
|
22
22
|
|
|
23
23
|
once(eventName, handler) {
|
|
24
|
-
let dispose: () => void = () => void 0
|
|
24
|
+
let dispose: () => void = () => void 0
|
|
25
25
|
const wrapped: PluginChannelHandler = (event) => {
|
|
26
|
-
dispose()
|
|
27
|
-
handler(event)
|
|
28
|
-
}
|
|
26
|
+
dispose()
|
|
27
|
+
handler(event)
|
|
28
|
+
}
|
|
29
29
|
|
|
30
|
-
dispose = client.regChannel(eventName, wrapped)
|
|
31
|
-
return dispose
|
|
30
|
+
dispose = client.regChannel(eventName, wrapped)
|
|
31
|
+
return dispose
|
|
32
32
|
},
|
|
33
33
|
|
|
34
34
|
get raw() {
|
|
35
|
-
return client
|
|
36
|
-
}
|
|
37
|
-
}
|
|
35
|
+
return client
|
|
36
|
+
},
|
|
37
|
+
}
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
let cachedRendererChannel: IPluginRendererChannel | null = null
|
|
40
|
+
let cachedRendererChannel: IPluginRendererChannel | null = null
|
|
41
41
|
|
|
42
42
|
export function usePluginRendererChannel(): IPluginRendererChannel {
|
|
43
43
|
if (!cachedRendererChannel) {
|
|
44
|
-
cachedRendererChannel = createPluginRendererChannel()
|
|
44
|
+
cachedRendererChannel = createPluginRendererChannel()
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
return cachedRendererChannel
|
|
47
|
+
return cachedRendererChannel
|
|
48
48
|
}
|
package/plugin/sdk/clipboard.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { PluginClipboardHistoryResponse, PluginClipboardItem } from './types'
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
function ensurePluginChannel() {
|
|
4
4
|
const channel = (window as any)?.$channel
|
|
5
5
|
if (!channel) {
|
|
6
6
|
throw new Error('[Plugin SDK] Clipboard channel requires plugin renderer context with $channel available.')
|
|
@@ -8,13 +8,15 @@ const ensurePluginChannel = () => {
|
|
|
8
8
|
return channel
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
if (!item)
|
|
11
|
+
function normalizeItem(item: PluginClipboardItem | null): PluginClipboardItem | null {
|
|
12
|
+
if (!item)
|
|
13
|
+
return item
|
|
13
14
|
if (!item.meta && typeof item.metadata === 'string') {
|
|
14
15
|
try {
|
|
15
16
|
const parsed = JSON.parse(item.metadata)
|
|
16
17
|
return { ...item, meta: parsed }
|
|
17
|
-
}
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
18
20
|
return { ...item, meta: null }
|
|
19
21
|
}
|
|
20
22
|
}
|
|
@@ -61,7 +63,7 @@ export function useClipboardHistory() {
|
|
|
61
63
|
: []
|
|
62
64
|
return {
|
|
63
65
|
...response,
|
|
64
|
-
history
|
|
66
|
+
history,
|
|
65
67
|
}
|
|
66
68
|
},
|
|
67
69
|
|
|
@@ -94,6 +96,6 @@ export function useClipboardHistory() {
|
|
|
94
96
|
return Boolean((response as any).success)
|
|
95
97
|
}
|
|
96
98
|
return true
|
|
97
|
-
}
|
|
99
|
+
},
|
|
98
100
|
}
|
|
99
101
|
}
|
package/plugin/sdk/common.ts
CHANGED
|
@@ -17,8 +17,10 @@ export function regShortcut(key: string, func: Function): boolean {
|
|
|
17
17
|
const channel = genChannel()
|
|
18
18
|
|
|
19
19
|
const res = channel.sendSync('shortcon:reg', { key })
|
|
20
|
-
if (res instanceof String)
|
|
21
|
-
|
|
20
|
+
if (res instanceof String)
|
|
21
|
+
throw new Error(String(res))
|
|
22
|
+
if (res === false)
|
|
23
|
+
return false
|
|
22
24
|
|
|
23
25
|
channel.regChannel('shortcon:trigger', ({ data }) => key === data.key && func())
|
|
24
26
|
|
|
@@ -33,16 +35,17 @@ export function regShortcut(key: string, func: Function): boolean {
|
|
|
33
35
|
*/
|
|
34
36
|
export async function communicateWithPlugin(
|
|
35
37
|
key: string,
|
|
36
|
-
info: any = {}
|
|
38
|
+
info: any = {},
|
|
37
39
|
): Promise<any> {
|
|
38
40
|
const channel = genChannel()
|
|
39
41
|
|
|
40
42
|
try {
|
|
41
43
|
return await channel.send('index:communicate', {
|
|
42
44
|
key,
|
|
43
|
-
info
|
|
45
|
+
info,
|
|
44
46
|
})
|
|
45
|
-
}
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
46
49
|
console.error(`[Plugin SDK] Failed to communicate`, error)
|
|
47
50
|
throw error
|
|
48
51
|
}
|
|
@@ -59,7 +62,8 @@ export async function sendMessage(message: string, data: any = {}): Promise<any>
|
|
|
59
62
|
|
|
60
63
|
try {
|
|
61
64
|
return await channel.send(`plugin:${message}`, data)
|
|
62
|
-
}
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
63
67
|
console.error(`[Plugin SDK] Failed to send message: ${message}`, error)
|
|
64
68
|
throw error
|
|
65
69
|
}
|
package/plugin/sdk/core-box.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { IPluginRendererChannel } from './types'
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
function ensurePluginContext(): { channel: IPluginRendererChannel, pluginName: string } {
|
|
4
4
|
const plugin = (window as any)?.$plugin
|
|
5
5
|
if (!plugin?.name) {
|
|
6
6
|
throw new Error('[TouchSDK] Unable to resolve plugin name inside renderer context.')
|
|
@@ -13,7 +13,7 @@ const ensurePluginContext = (): { channel: IPluginRendererChannel; pluginName: s
|
|
|
13
13
|
|
|
14
14
|
return {
|
|
15
15
|
channel,
|
|
16
|
-
pluginName: plugin.name as string
|
|
16
|
+
pluginName: plugin.name as string,
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
|
|
@@ -24,4 +24,3 @@ export async function clearCoreBoxItems(): Promise<void> {
|
|
|
24
24
|
const { channel, pluginName } = ensurePluginContext()
|
|
25
25
|
await channel.send('core-box:clear-items', { pluginName })
|
|
26
26
|
}
|
|
27
|
-
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DivisionBox SDK for Plugin Development
|
|
3
|
+
*
|
|
4
|
+
* Provides a simple API for plugins to create and manage DivisionBox instances.
|
|
5
|
+
* DivisionBox is a lightweight floating window container for plugin UIs and tools.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
CloseOptions,
|
|
10
|
+
DivisionBoxConfig,
|
|
11
|
+
DivisionBoxState,
|
|
12
|
+
SessionInfo,
|
|
13
|
+
} from '../../types/division-box'
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* State change event handler
|
|
17
|
+
*/
|
|
18
|
+
export type StateChangeHandler = (data: {
|
|
19
|
+
sessionId: string
|
|
20
|
+
state: DivisionBoxState
|
|
21
|
+
}) => void
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* DivisionBox SDK interface for plugins
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* // Open a DivisionBox
|
|
29
|
+
* const { sessionId } = await plugin.divisionBox.open({
|
|
30
|
+
* url: 'https://example.com/tool',
|
|
31
|
+
* title: 'My Tool',
|
|
32
|
+
* size: 'medium',
|
|
33
|
+
* keepAlive: true
|
|
34
|
+
* })
|
|
35
|
+
*
|
|
36
|
+
* // Listen for state changes
|
|
37
|
+
* plugin.divisionBox.onStateChange((data) => {
|
|
38
|
+
* console.log(`Session ${data.sessionId} is now ${data.state}`)
|
|
39
|
+
* })
|
|
40
|
+
*
|
|
41
|
+
* // Close the DivisionBox
|
|
42
|
+
* await plugin.divisionBox.close(sessionId)
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export interface DivisionBoxSDK {
|
|
46
|
+
/**
|
|
47
|
+
* Opens a new DivisionBox instance
|
|
48
|
+
*
|
|
49
|
+
* @param config - Configuration for the DivisionBox
|
|
50
|
+
* @returns Promise resolving to session information
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```typescript
|
|
54
|
+
* const { sessionId } = await plugin.divisionBox.open({
|
|
55
|
+
* url: 'https://example.com',
|
|
56
|
+
* title: 'Web Tool',
|
|
57
|
+
* icon: 'ri:tools-line',
|
|
58
|
+
* size: 'medium',
|
|
59
|
+
* keepAlive: true,
|
|
60
|
+
* header: {
|
|
61
|
+
* show: true,
|
|
62
|
+
* title: 'Custom Title',
|
|
63
|
+
* actions: [
|
|
64
|
+
* { label: 'Refresh', icon: 'ri:refresh-line', onClick: () => {} }
|
|
65
|
+
* ]
|
|
66
|
+
* }
|
|
67
|
+
* })
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
open(config: DivisionBoxConfig): Promise<SessionInfo>
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Closes a DivisionBox instance
|
|
74
|
+
*
|
|
75
|
+
* @param sessionId - The session ID to close
|
|
76
|
+
* @param options - Optional close options
|
|
77
|
+
* @returns Promise that resolves when closed
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```typescript
|
|
81
|
+
* // Simple close
|
|
82
|
+
* await plugin.divisionBox.close(sessionId)
|
|
83
|
+
*
|
|
84
|
+
* // Close with delay and animation
|
|
85
|
+
* await plugin.divisionBox.close(sessionId, {
|
|
86
|
+
* delay: 1000,
|
|
87
|
+
* animation: true
|
|
88
|
+
* })
|
|
89
|
+
*
|
|
90
|
+
* // Force close (ignore keepAlive)
|
|
91
|
+
* await plugin.divisionBox.close(sessionId, {
|
|
92
|
+
* force: true
|
|
93
|
+
* })
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
close(sessionId: string, options?: CloseOptions): Promise<void>
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Registers a state change listener
|
|
100
|
+
*
|
|
101
|
+
* @param handler - Callback function for state changes
|
|
102
|
+
* @returns Unsubscribe function
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```typescript
|
|
106
|
+
* const unsubscribe = plugin.divisionBox.onStateChange((data) => {
|
|
107
|
+
* console.log(`Session ${data.sessionId} changed to ${data.state}`)
|
|
108
|
+
* })
|
|
109
|
+
*
|
|
110
|
+
* // Later, unsubscribe
|
|
111
|
+
* unsubscribe()
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
onStateChange(handler: StateChangeHandler): () => void
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Updates session state data
|
|
118
|
+
*
|
|
119
|
+
* @param sessionId - The session ID
|
|
120
|
+
* @param key - State key
|
|
121
|
+
* @param value - State value
|
|
122
|
+
* @returns Promise that resolves when updated
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```typescript
|
|
126
|
+
* // Save scroll position
|
|
127
|
+
* await plugin.divisionBox.updateState(sessionId, 'scrollY', 150)
|
|
128
|
+
*
|
|
129
|
+
* // Save draft content
|
|
130
|
+
* await plugin.divisionBox.updateState(sessionId, 'draft', {
|
|
131
|
+
* text: 'Hello world',
|
|
132
|
+
* timestamp: Date.now()
|
|
133
|
+
* })
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
updateState(sessionId: string, key: string, value: any): Promise<void>
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Gets session state data
|
|
140
|
+
*
|
|
141
|
+
* @param sessionId - The session ID
|
|
142
|
+
* @param key - State key
|
|
143
|
+
* @returns Promise resolving to the state value
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* ```typescript
|
|
147
|
+
* const scrollY = await plugin.divisionBox.getState(sessionId, 'scrollY')
|
|
148
|
+
* const draft = await plugin.divisionBox.getState(sessionId, 'draft')
|
|
149
|
+
* ```
|
|
150
|
+
*/
|
|
151
|
+
getState(sessionId: string, key: string): Promise<any>
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Creates a DivisionBox SDK instance for plugin use
|
|
156
|
+
*
|
|
157
|
+
* @param channel - The plugin channel bridge for IPC communication
|
|
158
|
+
* @returns Configured DivisionBox SDK instance
|
|
159
|
+
*
|
|
160
|
+
* @internal
|
|
161
|
+
*/
|
|
162
|
+
export function createDivisionBoxSDK(channel: any): DivisionBoxSDK {
|
|
163
|
+
const stateChangeHandlers: Set<StateChangeHandler> = new Set()
|
|
164
|
+
|
|
165
|
+
// Register listener for state change events from main process
|
|
166
|
+
// The channel can be either IPluginChannelBridge (main process) or window.$channel (renderer)
|
|
167
|
+
const registerListener = () => {
|
|
168
|
+
if (channel.onMain) {
|
|
169
|
+
// Main process plugin context
|
|
170
|
+
channel.onMain('division-box:state-changed', (event: any) => {
|
|
171
|
+
stateChangeHandlers.forEach(handler => handler(event.data || event))
|
|
172
|
+
})
|
|
173
|
+
} else if (channel.on) {
|
|
174
|
+
// Renderer process context
|
|
175
|
+
channel.on('division-box:state-changed', (data: any) => {
|
|
176
|
+
stateChangeHandlers.forEach(handler => handler(data))
|
|
177
|
+
})
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
registerListener()
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
async open(config: DivisionBoxConfig): Promise<SessionInfo> {
|
|
185
|
+
// Send to main process
|
|
186
|
+
const sendFn = channel.sendToMain || channel.send
|
|
187
|
+
const result = await sendFn('division-box:open', config)
|
|
188
|
+
|
|
189
|
+
if (!result.success) {
|
|
190
|
+
throw new Error(result.error?.message || 'Failed to open DivisionBox')
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return result.data
|
|
194
|
+
},
|
|
195
|
+
|
|
196
|
+
async close(sessionId: string, options?: CloseOptions): Promise<void> {
|
|
197
|
+
const sendFn = channel.sendToMain || channel.send
|
|
198
|
+
const result = await sendFn('division-box:close', { sessionId, options })
|
|
199
|
+
|
|
200
|
+
if (!result.success) {
|
|
201
|
+
throw new Error(result.error?.message || 'Failed to close DivisionBox')
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
|
|
205
|
+
onStateChange(handler: StateChangeHandler): () => void {
|
|
206
|
+
stateChangeHandlers.add(handler)
|
|
207
|
+
|
|
208
|
+
return () => {
|
|
209
|
+
stateChangeHandlers.delete(handler)
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
|
|
213
|
+
async updateState(sessionId: string, key: string, value: any): Promise<void> {
|
|
214
|
+
const sendFn = channel.sendToMain || channel.send
|
|
215
|
+
const result = await sendFn('division-box:update-state', {
|
|
216
|
+
sessionId,
|
|
217
|
+
key,
|
|
218
|
+
value
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
if (!result.success) {
|
|
222
|
+
throw new Error(result.error?.message || 'Failed to update state')
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
|
|
226
|
+
async getState(sessionId: string, key: string): Promise<any> {
|
|
227
|
+
const sendFn = channel.sendToMain || channel.send
|
|
228
|
+
const result = await sendFn('division-box:get-state', {
|
|
229
|
+
sessionId,
|
|
230
|
+
key
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
if (!result.success) {
|
|
234
|
+
throw new Error(result.error?.message || 'Failed to get state')
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return result.data
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Hook for using DivisionBox SDK in plugin context
|
|
244
|
+
*
|
|
245
|
+
* @returns DivisionBox SDK instance
|
|
246
|
+
*
|
|
247
|
+
* @example
|
|
248
|
+
* ```typescript
|
|
249
|
+
* const divisionBox = useDivisionBox()
|
|
250
|
+
*
|
|
251
|
+
* const { sessionId } = await divisionBox.open({
|
|
252
|
+
* url: 'https://example.com',
|
|
253
|
+
* title: 'My Tool'
|
|
254
|
+
* })
|
|
255
|
+
* ```
|
|
256
|
+
*/
|
|
257
|
+
export function useDivisionBox(): DivisionBoxSDK {
|
|
258
|
+
// @ts-ignore - window.$channel is injected by the plugin system
|
|
259
|
+
const channel = window.$channel
|
|
260
|
+
|
|
261
|
+
if (!channel) {
|
|
262
|
+
throw new Error('[DivisionBox SDK] Channel not available. Make sure this is called in a plugin context.')
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return createDivisionBoxSDK(channel)
|
|
266
|
+
}
|